标题:多路复用的聊天工具小代码
只看楼主
遗矢的老人
Rank: 9Rank: 9Rank: 9
来 自:成都
等 级:蜘蛛侠
威 望:7
帖 子:325
专家分:1131
注 册:2012-7-20
结帖率:100%
已结贴  问题点数:20 回复次数:12 
多路复用的聊天工具小代码
服务端:
myhead.h
#ifndef _LIST_
#define _LIST_

#include <stdio.h>
#include <stdlib.h>      
#include <sys/types.h>      
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>
#include<strings.h>

typedef int data_t;
typedef struct node{
    data_t data;
    struct node *next;
}NODE;

NODE *creat_node(data_t data);
void insert(NODE *head, data_t data);
void _delete(NODE *head, data_t data);
void show_link(NODE *head);

#endif

list.c   //主要拿来存登陆用户的文件描述符

#include "myhead.h"

NODE *creat_node(data_t data)
{
    NODE *link_node = (NODE *)malloc(sizeof(NODE));
    if( NULL == link_node )
        exit(-1);
    link_node->data = data;
    link_node->next = NULL;
    return link_node;
}

void insert(NODE *head, data_t data)
{
    NODE *link_node = creat_node(data);
    while(NULL != head->next)
    {
        head = head->next;
    }
    head->next = link_node;
}

void _delete(NODE *head, data_t data)
{   
    NODE *p =NULL;
    while( head->next )
    {
        p = head;
        head = head->next;
        if( data == head->data )
        {
            p->next = head->next;
            free(head);
            return;
        }
    }
    printf("%d outwith the link!\n", data);   
}

void show_link(NODE *head)
{
    while( head->next )
    {   
        head = head->next;
        printf("Connected ID:%d\t", head->data);
    }
    printf("\n");
}

server.c
#include "myhead.h"

int main()
{
    int listenfd,connfd,maxfd,i,nbyte;
    struct sockaddr_in myaddr, cliaddr;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    fd_set global_rdfs,current_rdfs;
    char buf[10] = {0};
    if (0 > listenfd)
    {
        perror("socket");
        exit(-1);
    }
    int ii = 1;
    int sres = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &ii, sizeof(ii));
    if (0 > sres)
    {
        printf("setsockopt error\n");
        exit(-1);
    }

    memset(&myaddr, 0, sizeof(myaddr));
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    myaddr.sin_port = htons(8888);
    if(0 > bind(listenfd, (struct sockaddr *)&myaddr, sizeof(myaddr)))
    {
        perror("bind");
        exit(-1);
    }   
    listen(listenfd, 5);
    printf("listening...\n");
   

    FD_ZERO(&global_rdfs);
    FD_SET(listenfd, &global_rdfs);
    FD_SET(0, &global_rdfs);
    maxfd = listenfd;
    char send_buf[BUFSIZ] = {0};
    char recv_buf[BUFSIZ] = {0};
    int len = sizeof(cliaddr);
    int size = 0;
    NODE *head = creat_node(-1);
    while(1)
    {
        current_rdfs = global_rdfs;
        if(0 > select(maxfd + 1, &current_rdfs, NULL, NULL, 0))
        {
            perror("select");
            exit(-1);
        }
        else
        {
            for (i = 0; i <= maxfd; ++i)
            {
                if(FD_ISSET(i, &current_rdfs))
                {
                    if(i == listenfd)
                    {
                        connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
                        if(0 > connfd)
                        {
                            perror("accept");
                            exit(-1);
                        }
                        printf("Connection from %s\tport %d\t ID:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), connfd);
                        send(connfd, "welcome to server!", 30, 0);
                        FD_SET(connfd, &global_rdfs);
                        maxfd = maxfd > connfd ? maxfd : connfd;
                        insert(head, connfd);
                    }
                    else if(0 == i)
                    {   
                        printf("useage:id:msg\n");
                        show_link(head);
                        fgets(send_buf, sizeof(send_buf), stdin);
                        if(!strncmp(send_buf, "quit", 4))
                        {
                            printf("goodbye!\n");
                            exit(-1);
                        }
                        char *p = strstr(send_buf, ":");
                        size = p - send_buf + 1;
                        snprintf(buf, size, "%s", send_buf);
                        send(atoi(buf), send_buf + size, strlen(send_buf) - size, 0);
                    }
                    else
                    {
                        if(0 >= (nbyte= recv(i, recv_buf, sizeof(recv_buf), 0)))
                        {
                            _delete(head,i);
                            close(i);
                            FD_CLR(i, &global_rdfs);
                        }
                        else
                            printf("recv%d:%s\n", i, recv_buf);
                        
                    }
                }
            }
        }
    }
    return 0;
}

用户端:
client.c

#include <myhead.h>
#define MAXSIZE 1024

int main(int argc, char *argv[])  //带命令行ip
{
    if(2 != argc)
    {
        printf("useage:%s + server's ip\n");
        exit(-1);
    }
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(0 > sockfd)
    {
        perror("socket");
        exit(-1);
    }
   
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(argv[1]);
    servaddr.sin_port = htons(8888);
   
    char buf[MAXSIZE] = {0};   
        
    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    recv(sockfd, buf, sizeof(buf), 0);
    puts(buf);
   
    int i;
    fd_set readfd;
    int maxfd = -1;
    char recv_buf[MAXSIZE] = {0};
    char send_buf[MAXSIZE] = {0};
    while(1)
    {   
        FD_ZERO(&readfd);
        FD_SET(0, &readfd);
        maxfd = maxfd > 0 ? maxfd : 0;
        FD_SET(sockfd, &readfd);
        maxfd = maxfd > sockfd ? maxfd : sockfd;
        if(0 > select(maxfd + 1, &readfd, NULL, NULL, NULL))
        {
            perror("select");
            exit(-1);
        }

        for(i=0; i<=maxfd; i++)
        {
            if(FD_ISSET(i, &readfd))
            {
                if(i == sockfd)
                {
                    if(0 >= recv(sockfd, recv_buf, sizeof(recv_buf), 0))
                        exit(-1);
                    printf("recv:%s", recv_buf);
                    memset(recv_buf, 0, sizeof(recv_buf));
                }
                if(0 == i)
                {
                    fgets(send_buf, sizeof(send_buf), stdin);
                    send(sockfd, send_buf, strlen(send_buf) + 1, 0);
                    memset(send_buf, 0, sizeof(send_buf));
                }
            }
        }
    }
    return 0;
}
小弟socket编程差劲,目前写的只能1对多聊天,再准备写个好点的多对多聊天代码,各位大侠给点建议,不要多进程的,那个很浪费资源

[ 本帖最后由 遗矢的老人 于 2012-8-23 00:56 编辑 ]
搜索更多相关主题的帖子: void next include 聊天工具 insert 
2012-08-23 00:37
madfrogme
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:21
帖 子:1160
专家分:1106
注 册:2009-6-24
得分:7 
server.c里的
  send(atoi(buf), send_buf + size, strlen(send_buf) - size, 0);
不是很明白,可否解释一下

The quieter you become, the more you can hear
2012-08-23 13:42
遗矢的老人
Rank: 9Rank: 9Rank: 9
来 自:成都
等 级:蜘蛛侠
威 望:7
帖 子:325
专家分:1131
注 册:2012-7-20
得分:0 
回复 2楼 madfrogme
send(atoi(buf), send_buf + size, strlen(send_buf) - size, 0); 在终端输入的是文件描述符 fd + msg(即4:msg),但fd是字符,先提取出字符fd,用atoi转换成int型也就是文件描述符fd,size是这个(fd + : )的长度,send_buf + size 就是 msg啦
2012-08-23 15:22
遗矢的老人
Rank: 9Rank: 9Rank: 9
来 自:成都
等 级:蜘蛛侠
威 望:7
帖 子:325
专家分:1131
注 册:2012-7-20
得分:0 
服务端:
myhead.h                                   //头文件
#ifndef _LIST_
#define _LIST_   
 
#include <stdio.h>
#include <stdlib.h>      
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>
#include<strings.h>
 
typedef int data_t;
typedef struct node{                           //结构体,成员fd是装文件描述符file discretion
    data_t fd;
    data_t id;                                // id就是IDentity,这儿是用户登录时服务端给他分配一个ID号,没存在外部文件,所以只有客户退出连接就无效
    struct node *next;
}NODE;
 
NODE *creat_node(data_t fd, data_t id);                 //创建链表结点
void insert(NODE *head, data_t fd, data_t id);          //插入fd和id
NODE *find_fd(NODE *head, data_t id);                   //根据id找fd
NODE *find_id(NODE *head, data_t fd);                  //根据fd找id
void delete_fd(NODE *head, data_t fd);                 //用户退出时删除含fd节点
void show_id(NODE *head);                              //服务器浏览已连接的id,使用list命令
 
#endif
 
list.c   //具体的函数实现
 
#include "myhead.h"
 
NODE *creat_node(data_t fd , data_t id)
{
    NODE *link_node = (NODE *)malloc(sizeof(NODE));
    if( NULL == link_node )
        exit(-1);
    link_node->fd = fd;
    link_node->id = id;
    link_node->next = NULL;
    return link_node;
}
 
void insert(NODE *head, data_t fd, data_t id)
{
    NODE *link_node = creat_node(fd, id);
    while(NULL != head->next)
    {
        head = head->next;
    }
    head->next = link_node;
}
 
NODE *find_fd(NODE *head, data_t id)
{     
    while(head->next)
    {
        if(id == head->next->id)
            return head->next;
        head = head->next;
    }
    return NULL;
}
 
NODE *find_id(NODE *head, data_t fd)
{
    while(head->next)
    {
        if(fd == head->next->fd)
            return head->next;
        head = head->next;
    }
    return NULL;
}
 
void delete_fd(NODE *head, data_t fd)
{   
    NODE *p =NULL;
    while( head->next )
    {
        p = head;
        head = head->next;
        if( fd == head->fd )
        {
            p->next = head->next;
            free(head);
            return;
        }
    }
    printf("%d outwith the link!\n", fd);     
}
 
void show_id(NODE *head)
{
    printf("========================================================================\n");
    while( head->next )
    {   
        head = head->next;
         
        printf("Connected ID:%d\t", head->id);
    }
    printf("\n");
}
server.c
 
#include "myhead.h"
 
int main()
{
    int listenfd,connfd,maxfd,i,nbyte;                        //listenfd 是socket file discretion  connfd是客户连接文件描述符
    struct sockaddr_in myaddr, cliaddr;                        //sockaddr_in 是内核结构体,具体在man 7 ip
    listenfd = socket(AF_INET, SOCK_STREAM, 0);                 //创建打开ipv4内型TCP套接字文件
    fd_set global_rdfs,current_rdfs;  
    char buf[10] = {0};
    if (0 > listenfd)
    {
        perror("socket");
        exit(-1);
    }
    int ii = 1;
    int sres = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &ii, sizeof(ii));      //设置listenfd的属性,为IO复用型
    if (0 > sres)
    {
        printf("setsockopt error\n");
        exit(-1);
    }
 
    memset(&myaddr, 0, sizeof(myaddr));                                       //设置服务器分ip 及端口,内核的宏在man 7 ip 中查询
    myaddr.sin_family = AF_INET;                                             //   网络内型ipv4
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);                              //ip让内核取
    myaddr.sin_port = htons(8888);                                           //端口号为8888
    if(0 > bind(listenfd, (struct sockaddr *)&myaddr, sizeof(myaddr)))        //绑定ip及端口
    {
        perror("bind");
        exit(-1);
    }     
    listen(listenfd, 5);                                             //一次听五个客服连接,可以多设
    printf("listening...\n");
     
 
    FD_ZERO(&global_rdfs);                                         //global清零
    FD_SET(listenfd, &global_rdfs);                               //listenfd设为监听
    FD_SET(0, &global_rdfs);                                     //stdin设为监听
    maxfd = listenfd;                                            //文件描述符的最大值
    char send_buf[BUFSIZ] = {0};
    char recv_buf[BUFSIZ] = {0};
    int len = sizeof(cliaddr);
    int size = 0;
    int id = 0;
    NODE *head = creat_node(-1, 0);                              //链表头结点初始化
    while(1)
    {
        current_rdfs = global_rdfs;
        if(0 > select(maxfd + 1, &current_rdfs, NULL, NULL, 0))  //监听已设好的文件描述符
        {
            perror("select");
            exit(-1);
        }
        else
        {
            for (i = 0; i <= maxfd; ++i)
            {
                if(FD_ISSET(i, &current_rdfs))
                {
                    if(i == listenfd)
                    {
                        connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);                                         //监听到有新客户连接
                        if(0 > connfd)
                        {
                            perror("accept");
                            exit(-1);
                        }
                        id++;
                        printf("Connection from %s\tport %d\t ID:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), id); //把刚连接新客户的ip 和端口号打印子在服务端
                        char buf1[100] = "Welcome to server! you ID:";
                        char buf2[10] = {0};
                        snprintf(buf2, sizeof(data_t), "%d", id);                // 把id转化成字符串
                        strncat(buf1, buf2, strlen(buf2));                     //把字符id接在welcome to server后面
                        send(connfd, buf1, strlen(buf1), 0);                  //把给客户分配id发个客户
                        FD_SET(connfd, &global_rdfs);                         //把新增的文件描述符加入监听
                        maxfd = maxfd > connfd ? maxfd : connfd;             //取最大的文件描述符
                        insert(head, connfd, id);                            //把新增的文件描述符和id加在链表
                    }
                    else if(0 == i)
                    {     
                        fgets(send_buf, sizeof(send_buf), stdin);         //监听到标准输入有数据
                        if(!strncmp(send_buf, "quit", 4))                 //如果输入quit则退出服务器
                        {   
                            printf("goodbye!\n");
                            exit(-1);
                        }
                        if(!strncmp(send_buf, "list", 4))                 //输入list则显示在线客户id
                        {
                            show_id(head);
                            continue;
                        }
                        if(!strncmp(send_buf, "BC@", 3))                   //广播 标致《BC@》
                        {   
                            NODE *p = head;
                            while(NULL != p->next)
                            {     
                                send(p->next->fd, send_buf + 3, strlen(send_buf) - 2, 0);
                                p = p->next;
                            }
                            continue;
                        }     
 
                        char *p = strstr(send_buf, "@");                     //输入中检测@符
                        if (!p)                                              //没检测到上面的情况,则提醒输入格式
                        {
                            printf("useage:<id@msg>\n");
                            continue;
                        }
                        size = p - send_buf + 1;                           //检测到@符后把@之前的字符id长度算出为size
                        snprintf(buf, size, "%s", send_buf);              //剪取字符id放在buf
                        if(NULL == find_fd(head, atoi(buf)))              //atoi(buf)把字符id转化成int型,去找文件描述符fd
                        {
                            printf("The user doesn't have online\n");      //检测这个id是否在链表里,没在就提醒此id没在线
                            continue;
                        }
                        send(find_fd(head, atoi(buf))->fd, send_buf + size, strlen(send_buf) - size, 0);  //排除以上,在线id,把@后面的msg发给此id
                    }
                    else
                    {
                        if(0 >= (nbyte= recv(i, recv_buf, sizeof(recv_buf), 0)))                         //监听到接收端有消息
                        {
                            delete_fd(head,i);                                                            //id下线情况,则从链表中删除此NODE
                            close(i);                                                                     //关闭文件描述符
                            FD_CLR(i, &global_rdfs);                                                      //不再监听此fd
                        }
                        else
                            printf("recv<ID:%d>msg:\n%s", find_id(head, i)->id, recv_buf);              //接收到有msg情况,id号标志打印在终端
                    }
                }
            }
        }
    }
    return 0;
}
再修改了下 id 和 fd 分开,之前表示抱歉,我一些完,运行下没注释就发上来,客户端注释差不多

[ 本帖最后由 遗矢的老人 于 2012-8-24 11:28 编辑 ]
2012-08-23 15:30
madfrogme
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:21
帖 子:1160
专家分:1106
注 册:2009-6-24
得分:0 
请楼主坚定此贴必火的信心,

不过注释太少不利于此贴火起来

The quieter you become, the more you can hear
2012-08-23 18:54
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
得分:7 
火前留名
2012-08-23 19:27
遗矢的老人
Rank: 9Rank: 9Rank: 9
来 自:成都
等 级:蜘蛛侠
威 望:7
帖 子:325
专家分:1131
注 册:2012-7-20
得分:0 
回复 5楼 madfrogme
我写完就发上来了,就是没时间解释,下来我会解释的,你们给点意见啊,我准备写个多对多聊天的
2012-08-23 19:51
madfrogme
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:21
帖 子:1160
专家分:1106
注 册:2009-6-24
得分:0 
我觉得在写多对多之前你倒是可以看看客户端
 if(0 == i)
                {
                    fgets(send_buf, sizeof(send_buf), stdin);
                    send(sockfd, send_buf, strlen(send_buf) + 1, 0);
                    memset(send_buf, 0, sizeof(send_buf));

这几行代码
修改一下,加强一下程序的robustness
比如fgets, send之类的阻塞问题
当然具体参照 unp了, 当然只是建议

还有,你这头像!"#$%'()0

[ 本帖最后由 madfrogme 于 2012-8-23 21:42 编辑 ]

The quieter you become, the more you can hear
2012-08-23 20:06
遗矢的老人
Rank: 9Rank: 9Rank: 9
来 自:成都
等 级:蜘蛛侠
威 望:7
帖 子:325
专家分:1131
注 册:2012-7-20
得分:0 
回复 8楼 madfrogme
谢谢
2012-08-24 00:34
遗矢的老人
Rank: 9Rank: 9Rank: 9
来 自:成都
等 级:蜘蛛侠
威 望:7
帖 子:325
专家分:1131
注 册:2012-7-20
得分:0 
回复 6楼 zklhp
帮忙顶下
2012-08-24 00:45



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-379961-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.089461 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved