io多路复用之select

12 篇文章 1 订阅

1. select函数

该函数默认是阻塞的,在网络编程中要么阻塞在select/poll/epoll上, 要么阻塞在accept/recv等IO函数上。

       /* According to POSIX.1-2001, POSIX.1-2008 */
       #include <sys/select.h>

       /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

       void FD_CLR(int fd, fd_set *set);
       int  FD_ISSET(int fd, fd_set *set);
       void FD_SET(int fd, fd_set *set);
       void FD_ZERO(fd_set *set);

在linux中,fd的增长是连续的(除0,1,2外),所以利用对fd_set的bit位的设置,来确定想要的关注的事件(读事件,写事件和错误事件);

关于fd_set 在内核 include/uapi/linux/posix_types.h 中被定义:

 

单个进程下 select 监控的最大文件描述符个数是 1024 .

但是可以创建多个select以达到支持多连接的目的,但是在增加内存和增加机器性能的前提下,select 模型还是有无法突破 C10K 的问题。

当 FD_SET(listenfd, &rset)设置后, select函数就把要关注的 fd_set 集合,拷贝到内核中,然后等待内核准备就绪数据. 当内核中有可读、可写、出错事件后,内核就会准备就绪事件集合 fd_set,然后从内核拷贝到用户态,程序收到 fd_set 集合后做进一步的处理。

参数

nfds:是当前fd最大值+1

readfds: 当前关注的读事件的集合

writefds: 当前关注的写事件的集合

exceptfds: 当前关注的错误/异常事件的集合

struct timeval *timeou: 超时时间的设置

           struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };

timeout 是 NULL, 则select函数在没有读、写、异常事件时时阻塞的,一直等待;

timeout的值为0,: 没有事件不等待,立即返回;

timeout不为0: 等待一定时间后返回;

返回值

-1: 失败

0:超时
>0: 返回就绪的个数

fd 辅助函数

       void FD_CLR(int fd, fd_set *set); //从集合中删除fd
       int  FD_ISSET(int fd, fd_set *set); //查看fd在集合中是否被设置
       void FD_SET(int fd, fd_set *set); //fd 加入集合中
       void FD_ZERO(fd_set *set); //清空集合

测试程序

int main(int argc, char **argv)
{
  
    int listenfd, connfd, n;
    struct sockaddr_in servaddr;
    char buff[MAXLNE];                                                                                                                                                     

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(9999);

    int reuse = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
        return -1;
    }

    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    if (listen(listenfd, 10) == -1) {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
 
   fd_set rfds, rset, wfds, wset;
    FD_ZERO(&rfds);
    FD_ZERO(&wfds);

    FD_SET(listenfd, &rfds);
    int max_fds = listenfd;

    while (1) {
        struct timeval timeout;
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;

        rset = rfds;
        wset = wfds;

        /* int nready = select(max_fds+1, &rset, NULL, NULL, NULL); */
        int nready = select(max_fds+1, &rset, &wset, NULL, &timeout);                                                                                                      
        if (0 == nready) {
            continue;
        }   
        else if (nready < 0) {
            return -1; 
        }   

        if (FD_ISSET(listenfd, &rset)) {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
                printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
                return 0;

            }   
            
            FD_SET(connfd, &rfds);

            if (max_fds < connfd) {
                max_fds = connfd;
            }   

            if (--nready == 0) continue;
        }   
       
        int i = 0;
        for (i = listenfd+1; i <= max_fds; i++) {
            if (FD_ISSET(i, &rset)) {
                n = recv(i, buff, MAXLNE, 0);
                if (n > 0) {
                    buff[n] = '\0';
                    printf("recv msg from client: %s\n", buff);

                    /* send(i, buff, n, 0); */
                    FD_SET(i, &wfds);
                }
                else if (n == 0) {
                    FD_CLR(i, &rfds);
                    printf("disconnect. \n");
                    close(i);
                }
                if (--nready == 0) break;
            }
            else if (FD_ISSET(i, &wset)) {
                send(i, buff, n, 0);
                FD_SET(i, &rfds);
            }
        }
    }

    close(listenfd);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值