I/O多路复用(一):用select实现的多客户聊天的服务器

以下例子为用select实现的多客户聊天的服务器,客户端成功连接服务器之后,可以和其他客户进行群聊。

服务器代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>

void main(void)
{
    struct sockaddr_in ser;
    struct sockaddr_in cli;
    int ser_fd;
    int cli_len = sizeof(cli);

    bzero(&ser, sizeof(ser));
    bzero(&cli, sizeof(cli));

    ser.sin_family = AF_INET;
    ser.sin_port = htons(9678);
    ser.sin_addr.s_addr = htonl(INADDR_ANY);
    
    ser_fd = socket(AF_INET, SOCK_STREAM, 0);

    bind(ser_fd, (struct sockaddr*)&ser, sizeof(ser));

    listen(ser_fd, 5);

    int i;
    int client[FD_SETSIZE];
    for(i = 0; i < FD_SETSIZE; i++){  //初始化客户端数组,数组用于存放客户端socket描述符
        client[i] = -1;
    }


    fd_set rd;
    fd_set allset;

    FD_ZERO(&allset);
    FD_ZERO(&rd);
    FD_SET(ser_fd, &allset);

    int res;
    int maxfd = ser_fd;          //最大的描述符
    int confd;   //连接后返回的socket描述符
    int maxi = 0;
    int cli_num = 0;             //处于连接状态的客户端总数
    int self = 0;
    char buf[1024];
 
    while(1){
        rd = allset;  //见本文末尾红色提示
        res = select(maxfd + 1, &rd, NULL, NULL, NULL);
        if(res < 0)
            perror("select failed");
        else if(0 == res)
            printf("select time out\n");
        else{
            if(FD_ISSET(ser_fd, &rd)){
                confd = accept(ser_fd, (struct sockaddr*)&cli, &cli_len);
                if(-1 == confd){
                    perror("accept failed");
                    continue;
                }

                for(i = 0; i < FD_SETSIZE; i++){
                    if(client[i] < 0){
                        client[i] = confd;
                        break;
                    }
                }
                if(i > maxi)
                    maxi = i;
                if(maxfd < confd )           //获取最大的描述符
                    maxfd = confd;

                FD_SET(confd, &allset);
                cli_num++;

                printf("%d have connected.\n", confd);
                printf("Now,the client num is %d\n", cli_num);
            }

            for(i = 0; i <= maxi; i++){
                if(client[i] < 0)
                    continue;

                if(FD_ISSET(client[i], &rd)){
                    res = read(client[i], buf, sizeof(buf));
                    if(res <= 0){                //读取失败,去除该套接字
                        perror("read failed");
                        FD_CLR(client[i], &allset);
                        cli_num--;
                        printf("client %d is out of connect\n", client[i]);
                        close(client[i]);
                        client[i] = -1;
                    }else if(res > 0){
                 self = client[i];
                        printf("from client %d : %s\n", client[i], buf);
                        for(i = 0; i <= maxi; i++){            //实现群发
                            if(client[i] < 0)
                                continue;
                    if(self == client[i])                     
                      continue;
                            if(0 >= send(client[i], buf, sizeof(buf), 0))
                                perror("send failed\n");
                        }
                        memset(buf, 0, sizeof(buf));
                    }
                }
            }
        }
    }
}

 

 

 

客户端代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <string.h>
#include <netdb.h>
#include <stdlib.h>

void main(void)
{
    struct sockaddr_in local;
    struct hostent *he = gethostbyname("localhost");

    bzero(&local, sizeof(local));

    local.sin_family = AF_INET;
    local.sin_port = htons(9678);
    local.sin_addr = *(struct in_addr*)he->h_addr;

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    int res = connect(fd, (struct sockaddr*)&local, sizeof(local));
    if(res < 0){
        perror("connect failed");
        exit(1);
    }
    if(res == 0)
        printf("connect\n");


    fd_set rd;
    fd_set test;
    FD_ZERO(&rd);
    FD_ZERO(&test);
    FD_SET(0, &rd);
    FD_SET(fd, &rd);
    char buf[1024] = {0};

    while(1){
        test = rd;      //见本文末尾红色提示
        res = select(fd + 1, &test, NULL, NULL, NULL);
        if(res < 0)
            perror("select failed");
        else if(0 == res)
            perror("select time out");
        else{
            memset(buf, 0, sizeof(buf));
            if(FD_ISSET(0, &test)){
                fgets(buf, sizeof(buf), stdin);
                if(0 >= send(fd, buf, sizeof(buf), 0)){
                    perror("send failed\n");
                    break;
                }

            }
            if(FD_ISSET(fd, &test)){
                if(0 >= recv(fd, buf, sizeof(buf), 0)){
                    perror("recv failed\n");
                    break;
                }
                printf("recv : %s\n", buf);
            }
        }
    }
    FD_CLR(0, &rd);
    FD_CLR(fd, &rd);
    close(fd);
}

注意一点:调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件可读。

select系列的FD函数同样会修改fd_set的内容。

所以循环调用select时需用一个中间变量来储存初始fd_set的值。

 

select3个缺点:连接数受限 2 查找配对速度慢 3数据由内核拷贝到用户态。


                        


转载于:https://www.cnblogs.com/chris12/archive/2012/08/23/2653249.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值