zoukankan      html  css  js  c++  java
  • linux select用法

    select 是linux i/o 复用技术之一

    man 2 select

           #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);
    

    nfds是监听文件描述符的总数。它通常被设置为select监听的所有文件描述符的最大值加1.

    readfds, writefds, exceptfds指向可读、可写、异常等事件对应的文件描述符集合。应用程序调用select时,通过这3个参数,传入自己感兴趣的文件描述符。select返回时,内核通过修改它们来通知应用程序哪些文件描述符已经就绪。我们看到,应用程序和内核都修改了这些参数。

    fd_set 是结构体,定义如下:

    typedef struct
      {
        /* XPG4.2 requires this member name.  Otherwise avoid the name
           from the global namespace.  */
    #ifdef __USE_XOPEN
        __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
    # define __FDS_BITS(set) ((set)->fds_bits)
    #else
        __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
    # define __FDS_BITS(set) ((set)->__fds_bits)
    #endif
      } fd_set;
    

      该结构体只包含一个整形数组。其中,每个元素的一个位,标记一个文件描述符。fd_set 能容纳的文件描述符数量由__FD_SETSIZE决定,默认1024.所以select能处理的最大文件描述符个数为1024.

    对这个结构体的操作使用位运算。select.h文件中,提供了用来操作的宏

    # define __FD_ZERO(set)  
      do {                                        
        unsigned int __i;                                 
        fd_set *__arr = (set);                            
        for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i)          
          __FDS_BITS (__arr)[__i] = 0;                        
      } while (0)
    
    
    #define __FD_SET(d, set) 
      ((void) (__FDS_BITS (set)[__FD_ELT (d)] |= __FD_MASK (d)))
    #define __FD_CLR(d, set) 
      ((void) (__FDS_BITS (set)[__FD_ELT (d)] &= ~__FD_MASK (d)))
    #define __FD_ISSET(d, set) 
      ((__FDS_BITS (set)[__FD_ELT (d)] & __FD_MASK (d)) != 0)
    

      timeout参数设置超时时间,这是timeval结构体。使用指针是因为内核将修改它来告诉应用程序select等了多久。

    struct timeval {
        __kernel_time_t     tv_sec;     /* seconds */
        __kernel_suseconds_t    tv_usec;    /* microseconds */
    };
    

      如果timeout变量结构体成员都是0,则select立即返回。timeout为NULL,select一直阻塞,直到有文件描述符就绪。

    select成功时返回就绪文件描述符总数。如果超时时间内没有就绪的,返回0. select失败时返回-1 并设置errno。如果select等待期间,程序收到信号,select立即返回-1,并设置errno为EINTR。

    例子代码,服务端程序:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/select.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <errno.h>
    
    #include <set>
    
    using namespace std;
    
    #define BUFSIZE 10
    
    int createSocket()
    {
        struct sockaddr_in servaddr;
        int listenfd = -1;
    
        if (-1 == (listenfd = socket(PF_INET, SOCK_STREAM, 0)))
        {
            fprintf(stderr, "socket: %d, %s
    ", errno, strerror(errno));
            exit(1);
        }
    
        int reuseaddr = 1;
        if (-1 == setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)))
        {
            fprintf(stderr, "setsockopt: %d, %s
    ", errno, strerror(errno));
            exit(1);
        }
    
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = PF_INET;
        servaddr.sin_port = htons(8008);
        inet_pton(PF_INET, "0.0.0.0", &servaddr.sin_addr);
    
        if (-1 == bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)))
        {
            fprintf(stderr, "bind: %d, %s
    ", errno, strerror(errno));
            exit(1);
        }
    
        if (-1 == listen(listenfd, 5))
        {
            fprintf(stderr, "listen: %d, %s
    ", errno, strerror(errno));
            exit(1);
        }
    
        return listenfd;
    }
    
    int main(int argc, char const *argv[])
    {
        fd_set rfds;
        char buf[BUFSIZE] = {0};
        int listenfd = createSocket();
    
        // 保存所有的文件描述符
        set<int> fdset;
        fdset.insert(listenfd);
    
        while (1)
        {
            FD_ZERO(&rfds);
            // 每次都要重新设置rfds.因为select返回时,rfds被内核改变,里面只保存了就绪的文件描述符
            for (int fd : fdset)
            {
                FD_SET(fd, &rfds);
            }
            int ret = select(*fdset.rbegin()+1, &rfds, NULL, NULL, NULL);
            if (ret > 0)
            {
                for (int fd : fdset)
                {
                    // 有新的连接
                    if (fd == listenfd && FD_ISSET(fd, &rfds))
                    {
                        struct sockaddr_in client;
                        socklen_t addrlen = sizeof(client);
                        int cfd = -1;
    
                        if (-1 == (cfd = accept(listenfd, (struct sockaddr*)&client, &addrlen)))
                        {
                            fprintf(stderr, "accept: %d, %s
    ", errno, strerror(errno));
                            exit(1);
                        }
                        // 添加到 fd_set 结构体,并记录到 set
                        FD_SET(cfd, &rfds);
                        fdset.insert(cfd);
                        printf("get connection fd: %d
    ", cfd);
                    }
                    else if (FD_ISSET(fd, &rfds))
                    {
                        int lenrecv = -1;
                        memset(buf, 0, BUFSIZE);
    
                        lenrecv = recv(fd, buf, BUFSIZE-1, 0);
                        if (lenrecv > 0)
                        {
                            printf("%s
    ", buf);
                        }
                        else if (0 == lenrecv)
                        {
                            // 客户端退出,删除文件描述符,并关闭
                            fdset.erase(fd);
                            FD_CLR(fd, &rfds);
                            printf("delete connection fd: %d
    ", fd);
                            close(fd);
                        }
                        else
                        {
                            fprintf(stderr, "recv: %d, %s
    ", errno, strerror(errno));
                            exit(1);
                        }
                        
                    }
                }
            }
            else
            {
                fprintf(stderr, "select: %d, %s
    ", errno, strerror(errno));
                exit(1);
            }
        }
    
        close(listenfd);
        
        return 0;
    }
    

      

  • 相关阅读:
    集合类List,set,Map 的遍历方法,用法和区别
    如何优化sql语句
    io流(详询请加qq:2085920154)
    Servlet过滤器(详询请加qq:2085920154)
    oracle,sqlserver同一个表两个字段和成一个列查询 (详询请加qq:2085920154)
    SQL SERVER 将表中字符串转换为数字的函数 (详询请加qq:2085920154)
    SQL SERVER增加、删除、更改表中的字段名 (详询请加qq:2085920154)
    SQL Server日期时间格式转换字符串详解 (详询请加qq:2085920154)
    把文件打成zip或然rar下载 (详询请加qq:2085920154)
    计算机网络-校招总结
  • 原文地址:https://www.cnblogs.com/zuofaqi/p/9622860.html
Copyright © 2011-2022 走看看