精华内容
下载资源
问答
  • select函数详解

    万次阅读 多人点赞 2019-01-23 10:43:15
    select函数详解背景说明定义介绍、参数说明原理返回值pselect总结案例案例1案例2 说明:本文整合网络资源和man帮助文档,请酌情参考。 背景 select函数是实现IO多路复用的一种方式。 什么是IO多路复用? 举一个简单...

    说明:本文整合网络资源和man帮助文档,请酌情参考。

    fengjingtu

    背景

    select函数是实现IO多路复用的一种方式。

    什么是IO多路复用?

    举一个简单地网络服务器的例子,如果你的服务器需要和多个客户端保持连接,处理客户端的请求,属于多进程的并发问题,如果创建很多个进程来处理这些IO流,会导致CPU占有率很高。所以人们提出了I/O多路复用模型:一个线程,通过记录I/O流的状态来同时管理多个I/O

    select只是IO复用的一种方式,其他的还有:poll,epoll等。

    说明

    定义
    
    /* According to POSIX.1-2001 */
    #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);
    
    
    介绍、

    select()函数允许程序监视多个文件描述符,等待所监视的一个或者多个文件描述符变为“准备好”的状态。所谓的”准备好“状态是指:文件描述符不再是阻塞状态,可以用于某类IO操作了,包括可读,可写,发生异常三种。


    我们使用select来监视文件描述符时,要向内核传递的信息包括:
    ​ 1、我们要监视的文件描述符个数
    ​ 2、每个文件描述符,我们可以监视它的一种或多种状态,包括:可读,可写,发生异常三种。
    ​ 3、要等待的时间,监视是一个过程,我们希望内核监视多长时间,然后返回给我们监视结果呢?
    ​ 4、监视结果包括:准备好了的文件描述符个数,对于读,写,异常,分别是哪儿个文件描述符准备好了。

    参数说明

    **nfds:**是一个整数值, 表示集合中所有文件描述符的范围,即所有文件描述符的最大值+1。在windows中不需要管这个。

    linux select第一个参数的函数: 待测试的描述集的总个数。 但要注意, 待测试的描述集总是从0, 1, 2, …开始的。 所以, 假如你要检测的描述符为8, 9, 10, 那么系统实际也要监测0, 1, 2, 3, 4, 5, 6, 7, 此时真正待测试的描述符的个数为11个, 也就是max(8, 9, 10) + 1
    注意:
    ​ 1、果你要检测描述符8, 9, 10, 但是你把select的第一个参数定为8, 实际上只检测0到7, 所以select不会感知到8, 9, 10描述符的变化。
    ​ 2、果你要检测描述符8, 9, 10, 且你把select的第一个参数定为11, 实际上会检测0-10, 但是, 如果你不把描述如0 set到描述符中, 那么select也不会感知到0描述符的变化。
    ​ 所以, select感知到描述符变化的必要条件是, 第一个参数要合理, 比如定义为fdmax+1, 且把需要检测的描述符set到描述集中。

    fd_set:
    一个文件描述符集合保存在fd_set变量中,可读,可写,异常这三个描述符集合需要使用三个变量来保存,分别是 readfds,writefds,exceptfds。我们可以认为一个fd_set变量是由很多个二进制构成的数组,每一位表示一个文件描述符是否需要监视。

    对于fd_set类型的变量,我们只能使用相关的函数来操作。

    void FD_CLR(int fd, fd_set *set);//清除某一个被监视的文件描述符。
    int  FD_ISSET(int fd, fd_set *set);//测试一个文件描述符是否是集合中的一员
    void FD_SET(int fd, fd_set *set);//添加一个文件描述符,将set中的某一位设置成1;
    void FD_ZERO(fd_set *set);//清空集合中的文件描述符,将每一位都设置为0;
    

    使用案例:

    fd_set readfds;
    int fd;
    FD_ZERO(&readfds)//新定义的变量要清空一下。相当于初始化。
    FD_SET(fd,&readfds);//把文件描述符fd加入到readfds中。
    //select 返回
    if(FD_ISSET(fd,&readset))//判断是否成功监视
    {
        //dosomething
    }
    

    readfds:
    监视文件描述符的一个集合,我们监视其中的文件描述符是不是可读,或者更准确的说,读取是不是不阻塞了。
    writefds:
    监视文件描述符的一个集合,我们监视其中的文件描述符是不是可写,或者更准确的说,写入是不是不阻塞了。
    exceptfds:
    用来监视发生错误异常文件

    timeout

    struct timeval{
        long tv_sec;//秒
        long tv_usec;//微秒
    }
    

    timeout表示select返回之前的时间上限。
    如果timeout==NULL,无期限等待下去,这个等待可以被一个信号中断,只有当一个描述符准备好,或者捕获到一个信号时函数才会返回。如果是捕获到信号,select返回-1,并将变量errno设置成EINTR。

    如果timeout->tv_sec0 && timeout->tv_sec0 ,不等待直接返回,加入的描述符都会被测试,并且返回满足要求的描述符个数,这种方法通过轮询,无阻塞地获得了多个文件描述符状态。

    如果timeout->tv_sec!=0 || timeout->tv_sec!=0 ,等待指定的时间。当有描述符复合条件或者超过超时时间的话,函数返回。等待总是会被信号中断。

    原理

    理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
    ​ 执行fd_set set;FD_ZERO(&set);则set用位表示是0000,0000。
    ​ 若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
    ​ 若再加入fd=2,fd=1,则set变为0001,0011
    ​ 执行select(6,&set,0,0,0)阻塞等待
    ​ 若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

    返回值

    成功时:返回三中描述符集合中”准备好了“的文件描述符数量。
    超时:返回0
    错误:返回-1,并设置 errno

    EBADF:集合中包含无效的文件描述符。(文件描述符已经关闭了,或者文件描述符上已经有错误了)。
    EINTR:捕获到一个信号。
    EINVAL:nfds是负的或者timeout中包含的值无效。
    ENOMEM:无法为内部表分配内存。

    pselect
    #include <sys/select.h>
    
    int pselect(int nfds, fd_set *readfds, fd_set *writefds,
               fd_set *exceptfds, const struct timespec *timeout,
               const sigset_t *sigmask);
    
    
    

    select和pselect有三个主要的区别:

    1、select超时使用的是struct timeval,用秒和微秒计时,而pselect使用struct timespec ,用秒和纳秒。

    struct timespec{
        time_t tv_sec;//秒
        long tv_nsec;//纳秒
    }
    

    2、select会更新超时参数timeout 以指示还剩下多少时间,pselect不会。

    3、select没有sigmask参数.
    sigmask:这个参数保存了一组内核应该打开的信号(即:从调用线程的信号掩码中删除)

    当pselect的sigmask==NULL时pselect和select一样

    当sigmask!=NULL时,等效于以下原子操作:

    sigset_t origmask;
    sigprocmask(SIG_SETMASK, &sigmask, &origmask);
    ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
    sigprocmask(SIG_SETMASK, &origmask, NULL);
    

    接收信号的程序通常只使用信号处理程序来引发全局标志。全局标志将指示事件必须被处理。在程序的主循环中。一个信号将导致select和pselect返回-1 并将erron=EINTR。

    我们经常要在主循环中处理信号,主循环的某个位置将会检查全局标志,那么我们会问:如果信号在条件之后,select之前到达怎么办。答案是select会无限期阻塞。

    这种情况很少见,但是这就是为什么出现了pselect。因为他是类似原子操作的。

    举个栗子:

     static volatile sig_atomic_t got_SIGCHLD = 0;
    
           static void
           child_sig_handler(int sig)
           {
               got_SIGCHLD = 1;
           }
    
           int
           main(int argc, char *argv[])
           {
               sigset_t sigmask, empty_mask;
               struct sigaction sa;
               fd_set readfds, writefds, exceptfds;
               int r;
    
               sigemptyset(&sigmask);
               sigaddset(&sigmask, SIGCHLD);
               if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == -1) {
                   perror("sigprocmask");
                   exit(EXIT_FAILURE);
               }
    
               sa.sa_flags = 0;
               sa.sa_handler = child_sig_handler;
               sigemptyset(&sa.sa_mask);
               if (sigaction(SIGCHLD, &sa, NULL) == -1) {
                   perror("sigaction");
                   exit(EXIT_FAILURE);
               }
    
               sigemptyset(&empty_mask);
    
               for (;;) {          /* main loop */
                   /* Initialize readfds, writefds, and exceptfds
                      before the pselect() call. (Code omitted.) */
    
                   r = pselect(nfds, &readfds, &writefds, &exceptfds,
                               NULL, &empty_mask);
                   if (r == -1 && errno != EINTR) {
                       /* Handle error */
                   }
    
                   if (got_SIGCHLD) {
                       got_SIGCHLD = 0;
    
                       /* Handle signalled event here; e.g., wait() for all
                          terminated children. (Code omitted.) */
                   }
    
                   /* main body of program */
               }
           }
    
    

    总结

    select()可以同时监视多个描述符,如果他们没有活动,则正确地将进程置于休眠状态。Unix程序员们经常要处理多个文件描述符的I/O,他们的数据流可能是间歇性的。如果只创建read或者write会导致程序阻塞。

    在我们使用select的时候,需要注意:

    1、我们应该总是设置timeout=0,因为如果没有可用的数据,程序在运行时间里将无视可做。依赖超时的代码通常是不可移植,并且很难调试。

    2、nfds的值一要准备且适当。

    3、如果在调用完select之后,你不想检查结果,也不想做出适当的响应,那么文件描述符不需要添加到集合中。

    4、select返回后,所有的文件描述符都应该被检查,看看他们是否准备好了。

    5、read,recv,write,send,这几个函数不一定读/写你所请求的全部数据。如果他们读/写全部数据,是因为低流量负载和快速流。情况并非重视如此,应该处理你的函数仅管理发送或接收单个字节的情况。

    6、除非你真的确信你有少量的数据要处理,否则不要一次只读一个字节,当你每次都能缓冲的时候,尽可能多的读取数据是非常低效的。

    7、read,recv,write,send和select都会有返回-1的情况,并set errno的值。这些errno必须被恰当的处理。如果你的程序不会接收到任何信号,那么errno永远都不会等于EINTR,如果你的程序并不会设置非阻塞IO,那么errno就不会等于EAGAIN。

    8、调用read,recv,write,send,不要使buffer的长度为0;

    9、如果read,recv,write,send调用失败,并且返回的errno不是7中说的那两种情况,或者返回0,意思是“end-of-file”,这种情况下我们不应再将文件描述符传递给select。

    10、每次调用select之前,timeout都用重新设置。

    11、由于select()修改其文件描述符集,如果调用在循环中使用,则必须在每次调用之前重新初始化这些集。

    大多数的操作系统都支持select。相比于试图用线程,进程,IPCS,信号,内存共享等方式来解决问题,select函数更有效且轻松。系统调用poll和select相似,在监视稀疏文件集合的时候更加有效。poll现在也在被广泛的使用,但没有select简便。linux专用的epoll在监视大连数据时比select和poll更加有效。

    案例

    案例1

    下面是"man select "帮助文档中案例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main(void)
    {
        fd_set rfds;//定义一个能保存文件描述符集合的变量
        struct timeval tv;//定义超时时间
        int retval;//保存返回值
    
        /* Watch stdin (fd 0) to see when it has input. */
        /* 监测标准输入流(fd=0)看什么时候又输入*/
        FD_ZERO(&rfds);//初始化集合
        FD_SET(0, &rfds);//把文件描述符0加入到监测集合中。
    
        /* Wait up to five seconds. */
        /* 设置超时时间为5s */
        tv.tv_sec = 5;
        tv.tv_usec = 0;
    
        /*调用select函数,将文件描述符集合设置成读取监测 */
        retval = select(1, &rfds, NULL, NULL, &tv);
        /* Don't rely on the value of tv now! */
        /* 这时候的tv值是不可依赖的 */
    
        /*根据返回值类型判断select函数 */
        if (retval == -1)
            perror("select()");
        else if (retval)
            printf("Data is available now.\n");
        /* FD_ISSET(0, &rfds) will be true. */
        /* 因为值增加了一个fd,如果返回值>0,则说明fd=0在集合中。*/
        else
            printf("No data within five seconds.\n");
    
        exit(EXIT_SUCCESS);
    }
    
    
    案例2

    下面是"man select_tut "帮助文档中案例:

    这个例子更好的说明了select函数的作用,这是一个TCP转发相关的程序,从一个端口转发到另一个端口

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <string.h>
    #include <signal.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <errno.h>
    
    static int forward_port;
    
    #undef max
    #define max(x,y) ((x) > (y) ? (x) : (y))
    
    static int listen_socket(int listen_port)
    {
        struct sockaddr_in a;
        int s;
        int yes;
    
        if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            return -1;
        }
        yes = 1;
        if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
                       (char *) &yes, sizeof(yes)) == -1) {
            perror("setsockopt");
            close(s);
            return -1;
        }
        memset(&a, 0, sizeof(a));
        a.sin_port = htons(listen_port);
        a.sin_family = AF_INET;
        if (bind(s, (struct sockaddr *) &a, sizeof(a)) == -1) {
            perror("bind");
            close(s);
            return -1;
        }
        printf("accepting connections on port %d\n", listen_port);
        listen(s, 10);
        return s;
    }
    
    static int connect_socket(int connect_port, char *address)
    {
        struct sockaddr_in a;
        int s;
    
        if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            close(s);
            return -1;
        }
    
        memset(&a, 0, sizeof(a));
        a.sin_port = htons(connect_port);
        a.sin_family = AF_INET;
    
        if (!inet_aton(address, (struct in_addr *) &a.sin_addr.s_addr)) {
            perror("bad IP address format");
            close(s);
            return -1;
        }
    
        if (connect(s, (struct sockaddr *) &a, sizeof(a)) == -1) {
            perror("connect()");
            shutdown(s, SHUT_RDWR);
            close(s);
            return -1;
        }
        return s;
    }
    
    #define SHUT_FD1 do {                                \
                                if (fd1 >= 0) {                 \
                                    shutdown(fd1, SHUT_RDWR);   \
                                    close(fd1);                 \
                                    fd1 = -1;                   \
                                }                               \
                            } while (0)
    
    #define SHUT_FD2 do {                                \
                                if (fd2 >= 0) {                 \
                                    shutdown(fd2, SHUT_RDWR);   \
                                    close(fd2);                 \
                                    fd2 = -1;                   \
                                }                               \
                            } while (0)
    
    #define BUF_SIZE 1024
    
    int main(int argc, char *argv[])
    {
        int h;
        int fd1 = -1, fd2 = -1;
        char buf1[BUF_SIZE], buf2[BUF_SIZE];
        int buf1_avail, buf1_written;
        int buf2_avail, buf2_written;
    
        //我们希望调用主函数的时候,要指明,本地端口,发送端口,还有发送的ip地址
        if (argc != 4) {
            fprintf(stderr, "Usage\n\tfwd <listen-port> "
                    "<forward-to-port> <forward-to-ip-address>\n");
            exit(EXIT_FAILURE);
        }
    
        // 忽略SIGPIPE这个信号,这个信号常出现在网络编程中,访问一个已经关闭的文件描述符时候出现。
        signal(SIGPIPE, SIG_IGN);
    
        //确定发送端口
        forward_port = atoi(argv[2]);
    
        //监听本地端口
        h = listen_socket(atoi(argv[1]));
        if (h == -1)
            exit(EXIT_FAILURE);
    
        for (;;) {
            int r, nfds = 0;
            fd_set rd, wr, er;
    
            FD_ZERO(&rd);
            FD_ZERO(&wr);
            FD_ZERO(&er);
            FD_SET(h, &rd);
            // 获取nfds的值。并把fd1,fd2分别加入到,可读,可写,异常监视集合中去。
            nfds = max(nfds, h);
            if (fd1 > 0 && buf1_avail < BUF_SIZE) {
                FD_SET(fd1, &rd);
                nfds = max(nfds, fd1);
            }
            if (fd2 > 0 && buf2_avail < BUF_SIZE) {
                FD_SET(fd2, &rd);
                nfds = max(nfds, fd2);
            }
            if (fd1 > 0 && buf2_avail - buf2_written > 0) {
                FD_SET(fd1, &wr);
                nfds = max(nfds, fd1);
            }
            if (fd2 > 0 && buf1_avail - buf1_written > 0) {
                FD_SET(fd2, &wr);
                nfds = max(nfds, fd2);
            }
            if (fd1 > 0) {
                FD_SET(fd1, &er);
                nfds = max(nfds, fd1);
            }
            if (fd2 > 0) {
                FD_SET(fd2, &er);
                nfds = max(nfds, fd2);
            }
            
            //开始监视
            r = select(nfds + 1, &rd, &wr, &er, NULL);
    
            if (r == -1 && errno == EINTR)
                continue;
    
            if (r == -1) {
                perror("select()");
                exit(EXIT_FAILURE);
            }
    
            if (FD_ISSET(h, &rd)) {
                unsigned int l;
                struct sockaddr_in client_address;
    
                memset(&client_address, 0, l = sizeof(client_address));
                r = accept(h, (struct sockaddr *) &client_address, &l);
                if (r == -1) {
                    perror("accept()");
                } else {
                    SHUT_FD1;
                    SHUT_FD2;
                    buf1_avail = buf1_written = 0;
                    buf2_avail = buf2_written = 0;
                    fd1 = r;
                    fd2 = connect_socket(forward_port, argv[3]);
                    if (fd2 == -1)
                        SHUT_FD1;
                    else
                        printf("connect from %s\n",
                               inet_ntoa(client_address.sin_addr));
                }
            }
    
            /* NB: read oob data before normal reads */
    
            if (fd1 > 0)
                if (FD_ISSET(fd1, &er)) {
                    char c;
    
                    r = recv(fd1, &c, 1, MSG_OOB);
                    if (r < 1)
                        SHUT_FD1;
                    else
                        send(fd2, &c, 1, MSG_OOB);
                }
            if (fd2 > 0)
                if (FD_ISSET(fd2, &er)) {
                    char c;
    
                    r = recv(fd2, &c, 1, MSG_OOB);
                    if (r < 1)
                        SHUT_FD2;
                    else
                        send(fd1, &c, 1, MSG_OOB);
                }
            if (fd1 > 0)
                if (FD_ISSET(fd1, &rd)) {
                    r = read(fd1, buf1 + buf1_avail,
                             BUF_SIZE - buf1_avail);
                    if (r < 1)
                        SHUT_FD1;
                    else
                        buf1_avail += r;
                }
            if (fd2 > 0)
                if (FD_ISSET(fd2, &rd)) {
                    r = read(fd2, buf2 + buf2_avail,
                             BUF_SIZE - buf2_avail);
                    if (r < 1)
                        SHUT_FD2;
                    else
                        buf2_avail += r;
                }
            if (fd1 > 0)
                if (FD_ISSET(fd1, &wr)) {
                    r = write(fd1, buf2 + buf2_written,
                              buf2_avail - buf2_written);
                    if (r < 1)
                        SHUT_FD1;
                    else
                        buf2_written += r;
                }
            if (fd2 > 0)
                if (FD_ISSET(fd2, &wr)) {
                    r = write(fd2, buf1 + buf1_written,
                              buf1_avail - buf1_written);
                    if (r < 1)
                        SHUT_FD2;
                    else
                        buf1_written += r;
                }
    
            /* check if write data has caught read data */
    
            if (buf1_written == buf1_avail)
                buf1_written = buf1_avail = 0;
            if (buf2_written == buf2_avail)
                buf2_written = buf2_avail = 0;
    
            /* one side has closed the connection, keep
                      writing to the other side until empty */
    
            if (fd1 < 0 && buf1_avail - buf1_written == 0)
                SHUT_FD2;
            if (fd2 < 0 && buf2_avail - buf2_written == 0)
                SHUT_FD1;
        }
        exit(EXIT_SUCCESS);
    }
    
    

    上面的程序可以应用于大多数类型的TCP连接,包括telnet服务器对OOB信号的转发。它处理了同时在两个方向上流动这一棘手问题。你可能会想,使用连个进程不是更有效吗?事实上使用两个进程会更复杂。另一个想法是使用fcntl设置非阻塞的I/O使用,这也有弊端,因为它使用非常低效的超时。

    这个程序不能处理同时有多个连接的情况,但很容易扩展。你只需要为每个连接创建一个buffer。当前的程序中,新的连接会导致旧的连接被覆盖丢弃。

    展开全文
  • select函数

    千次阅读 2018-08-03 21:31:24
    select函数:  系统提供select函数来实现多路复用输入/输出模型。原型:  #include &lt;sys/time.h&gt;  #include &lt;unistd.h&gt;  select函数:  系统提供select函数来实现多路复用...

    select函数:

      系统提供select函数来实现多路复用输入/输出模型。原型:

      #include <sys/time.h>

      #include <unistd.h>

      select函数:

      系统提供select函数来实现多路复用输入/输出模型。原型:

      #include <sys/time.h>

      #include <unistd.h>

      int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);

      参数maxfd是需要监视的最大的文件描述符值+1;rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合。struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。

      FD_ZERO,FD_SET,FD_CLR,FD_ISSET: 参数maxfd是需要监视的最大的文件描述符值+1;rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合。struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。

      FD_ZERO,FD_SET,FD_CLR,FD_ISSET:

      FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。

      FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。

      FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。

      FD_ISSET(int fd,fd_set *fdset);用于测试指定的文件描述符是否在该集合中。

      struct timeval结构:

      struct timeval{

      long tv_sec;//second

      long tv_usec;//minisecond

      }

      timeout设置情况:

      null:select将一直被阻塞,直到某个文件描述符上发生了事件。

      0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。

      特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。

      --

      ('fd_set') 是一组文件描述符(fd)的集合。由于fd_set类型的长度在不同平台上不同,因此应该用一组标准的宏定义来处理此类变量如下:

    fd_set set;

    FD_ZERO(&set); /* 将set清零 */ 

    FD_SET(fd, &set); /* 将fd加入set */ 

    FD_CLR(fd, &set); /* 将fd从set中清除 */     

     FD_ISSET(fd, &set); /* 如果fd在set中则真 */

      在 过去,一个fd_set通常只能包含少于等于32个文件描述符,因为fd_set其实只用了一个int的比特矢量来实现,在大多数情况下,检查 fd_set能包括任意值的文件描述符是系统的责任,但确定你的fd_set到底能放多少有时你应该检查/修改宏FD_SETSIZE的值。*这个值是系 统相关的*,同时检查你的系统中的select() 的man手册。有一些系统对多于1024个文件描述符的支持有问题。

      多路复用的方式是真正实用的服务器程序,非多路复用的网络程序只能作为学习或着陪测的角色。本文说下个人

      接触过的多路复用函数:select/poll/epoll/port。kqueue的*nix系统没接触过,估计熟悉了上面

      四种,kqueue也只是需要熟悉一下而已。

     

     

    select模型

      select原型: int select(int n ,fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

      其中参数n表示监控的所有fd中最大值+1。

      和select模型紧密结合的四个宏,含义不解释了:

      FD_CLR(int fd, fd_set *set);

      FD_ISSET(int fd, fd_set *set);

      FD_SET(int fd, fd_set *set);

      FD_ZERO(fd_set *set);

      理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。

      (1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。

      (2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)

      (3)若再加入fd=2,fd=1,则set变为0001,0011

      (4)执行select(6,&set,0,0,0)阻塞等待

      (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

      基于上面的讨论,可以轻松得出select模型的特点

      (1)可监控的文件描述符个数取决与sizeof(fd_set)的值。我这边服务 器上sizeof(fd_set)=512,每bit表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096。据说可调,另有说虽 然可调,但调整上限受于编译内核时的变量值。本人对调整fd_set的大小不太感兴趣,可以有效突破select可监控的文件描述符上 限。

      (2)将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select 返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个 参数。

      (3)可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。

    展开全文
  • select函数的使用

    千次阅读 2019-04-04 14:04:27
    select函数的作用: select()在SOCKET编程中还是比较重要的,可是对于初学SOCKET的人来说都不太爱用select()写程序,他们只是习惯写诸如 conncet()、accept()、recv()或recvfrom这样的阻塞程序(所谓阻塞方式block,...

    select函数的作用:

    select()在SOCKET编程中还是比较重要的,可是对于初学SOCKET的人来说都不太爱用select()写程序,他们只是习惯写诸如 conncet()、accept()、recv()或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用select()就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况。如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

    实现异步IO:
    https://blog.csdn.net/gettogetto/article/details/51442914

    1.linux下使用

    头文件:

    #include <sys/select.h>
    

    函数原型:

    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
    

    参数:

    1. int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数值无所谓,可以设置不正确。
    2. fd_set* readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
    3. fd_set* writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
    4. fe_set* errorfds同上面两个参数的意图,用来监视文件错误异常。
    5. struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态。
         第一:若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
         第二:若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
         第三:timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

    返回值:

    • 负值:select错误;
    • 正值:某些文件可读写或出错;
    • 0:等待超时,没有可读写或错误的文件。

    错误码:

    执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。

    EBADF 文件描述词为无效的或该文件已关闭
    
    EINTR 此调用被信号所中断
    
    EINVAL 参数n为负值。
    
    ENOMEM 核心内存不足
    

    函数说明:

    select()用来等待文件描述词状态的改变。参数maxfdp代表最大的文件描述词加1,参数readfds、writefds和exceptfds 称为描述词组,是用来回传该描述词的读,写或例外的状况。底下的宏提供了处理这三种描述词组的方式:

    FD_CLR(inr fd,fd_set* set);		用来清除描述词组set中相关fd的位
    
    FD_ISSET(int fd,fd_set *set);	用来测试描述词组set中相关fd的位是否为真
    
    FD_SET(int fd,fd_set*set);		用来设置描述词组set中相关fd的位
    
    FD_ZERO(fd_set *set);			用来清除描述词组set的全部位
    

    结构体说明:

    1)struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。
    fd_set集合可以通过一些宏由人为来操作,比如

    清空集合FD_ZERO(fd_set *);
    将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set*);
    将一个给定的文件描述符从集合中删除FD_CLR(int,fd_set*);
    检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。
    

    2)struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是微妙数。如下所示:

    truct timeval
    {
    	time_t tv_sec;		//seconds 秒
    	time_t tv_usec;		//microseconds 微秒,1000000 微秒 = 1秒
    }; 
    //头文件
    #include <sys/time.h>
    

    示例:在标准输入读取9个字节数据

    #include <sys/time.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <assert.h>
    #include <string.h>
    
    #include <unistd.h>
    #include <iostream>
    using namespace std;
    
    int main(int argc, char ** argv)
    {
        int ret; // return val
        char buf[10] = "";
        fd_set rdfds;//
        struct timeval tv; //store timeout
    
    
        FD_ZERO(&rdfds); //clear rdfds
        FD_SET(1, &rdfds); //add stdin handle into rdfds
    
        tv.tv_sec = 5;
        tv.tv_usec = 500;
    
        ret = select(1 + 1, &rdfds, NULL, NULL, &tv);
    
        if(ret < 0)
            perror("\nselect");
        else if(ret == 0)
            printf("\ntimeout");
        else
        {
            printf("\nret=%d", ret);
        }
    
        if(FD_ISSET(1, &rdfds))
        {
            printf("\nreading\n");
            fread(buf, 9, 1, stdin); // read form stdin
        }
    
        // read(0, buf, 9); /* read from stdin */
        // fprintf(stdout, "%s\n", buf); /* write to stdout */
    
        write(1, buf, strlen(buf)); //write to stdout
    
        printf("\nRead character length is %ld.\n", strlen(buf));
    
        return 0;
    
    }
    

    示例:Linux下监控键盘上是否有数据到来?

    #include <sys/time.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <assert.h>
    
    #include <unistd.h>
    #include <iostream>
    using namespace std;
    
    int main ()
    {
        int keyboard;
        int ret,i;
        char c;
        fd_set readfd;
        struct timeval timeout;
        keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
        assert(keyboard>0);
        while(1)
        {
            timeout.tv_sec=1;
            timeout.tv_usec=0;
            FD_ZERO(&readfd);
            FD_SET(keyboard,&readfd);
     
            ///监控函数
            ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);
            if(ret == -1)   //错误情况
                cout<<"error"<<endl ;
            else if(ret)    //返回值大于0 有数据到来
                if(FD_ISSET(keyboard,&readfd))
                {
                    i=read(keyboard,&c,1);
                    if('\n'==c)
                        continue;
                    printf("hehethe input is %c\n",c);
                    if ('q'==c)
                        break;
                }
            else    //超时情况
            {
                cout<<"time out"<<endl;
                continue;
            }
        }
    }
    

    参考:https://blog.csdn.net/leo115/article/details/8097143

    示例:有了select后,可以写出像样的网络程序来!举个简单的例子,就是从网络上接受数据写入一个文件中。

    int main()
    {
        int sock;
        FILE *fp;
        struct fd_set fds;
        struct timeval timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0
        char buffer[256]={0}; //256字节的接收缓冲区
        /* 假定已经建立UDP连接,具体过程不写,简单,当然TCP也同理,主机ip和port都已经给定,要写的文件已经打开
        sock=socket(...);
        bind(...);
        fp=fopen(...); */
        while(1)
        {
            FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化
            FD_SET(sock,&fds); //添加描述符
            FD_SET(fp,&fds); //同上
            maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1
            switch(select(maxfdp,&fds,&fds,NULL,&timeout)) //select使用
            {
                case -1: exit(-1);break; //select错误,退出程序
                case 0:break; //再次轮询
                default:
                if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据
                {
                    recvfrom(sock,buffer,256,.....);//接受网络数据    
                    if(FD_ISSET(fp,&fds)) //测试文件是否可写
                    fwrite(fp,buffer...);//写入文件
                    //buffer清空;
                }// end if break;
            }// end switch
        }//end while
    }//end main
    

    socket编程示例:

    //m_confd为建立好的套接字
    fd_set rset;
    struct timeval select_timeout;
    
    while(true)
    {
    	//select_timeout每一次循环都需要设置,因为调用select会将select_timeout清空
    	select_timeout.tv_sec = 5;
    	select_timeout.tv_usec = 0;
    	
    	FD_ZERO(&rset);
    	FD_SET(m_confd, &rset);
    	//检测套接字是否可读,这里只检测读,若需要检测写和异常,填充第三和第四个参数即可
    	ret = select(m_confd+1, &rset, NULL, NULL, &select_timeout);
    	if(ret > 0)
    	{
    		if(FD_ISSET(m_confd, &rset))	//检测是否是m_confd套接口有数据可读
    		{
    			///接收数据
    		}
    		else
    		{
    			//说明是其他信号引起的
    		}
    	}
    	else if(0 == ret)	///超时
    	{
    		//超时未检测到可读信号
    	}
    	else				//异常
    	{
    		//退出线程
    	}
    }
    

    2.Windows下使用

    函数原型:

    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
    

    参数:

    1. int maxfdp 本参数忽略,仅起到兼容作用,设为0即可;。
    2. fd_set* readfds (可选)指针,指向一组等待可读性检查的套接口;
    3. fd_set* writefds (可选)指针,指向一组等待可写性检查的套接口;
    4. fe_set* errorfds (可选)指针,指向一组等待错误检查的套接口;
    5. struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态。
         第一:若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
         第二:若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
         第三:timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

    返回值:

    • 负值:select错误,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码
    • 正值:某些文件可读写或出错,调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;
    • 0:等待超时,没有可读写或错误的文件。

    参考资料:
    https://www.cnblogs.com/ccsccs/articles/4224253.html
    https://blog.csdn.net/gettogetto/article/details/51442914
    https://www.cnblogs.com/renyuan/p/5100184.html
    https://blog.csdn.net/flyinautumn/article/details/8088227
    https://blog.csdn.net/rootusers/article/details/43604729

    展开全文
  • C语言中select函数简介及使用

    千次阅读 2019-09-14 20:27:40
    select函数用来检查套接字描述符(sockets descriptors)是否已准备好读/写,提供了一种同时检查多个套接字的方法。 Linux中select函数的声明在/usr/include/x86_64-linux-gnu/sys/select.h文件中,Windows下select...

    select函数用来检查套接字描述符(sockets descriptors)是否已准备好读/,提供了一种同时检查多个套接字的方法。

    Linux中select函数的声明在/usr/include/x86_64-linux-gnu/sys/select.h文件中,Windows下select函数的声明在WinSock2.h文件中,声明如下:

    // Linux
    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    // Windows
    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);
    // macros
    FD_SET(int fd, fd_set *set); // Add fd to the set
    FD_CLR(int fd, fd_set *set); // Remove fd from the set
    FD_ISSET(int fd, fd_set *set); // Return true if fd is in the set
    FD_ZERO(fd_set *set); // Clear all entries from the set

    不像socket中connect、accept、recv这几个函数属于阻塞方式,而select函数属于非阻塞方式。在使用select函数时,会经常用到四个宏FD_SET(将一个指定的文件描述符加入集合)、FD_CLR(将一个指定的文件描述符从集合中删除)、FD_ISSET(检查一个指定的文件描述符是否在集合中)、FD_ZERO(清空集合)。类型fd_set存放的是一组文件描述符的集合,在Linux系统中,如设备、管道、FIFO等都可通过文件描述符的形式来访问。文件描述符在形式上是一个非负整数,实际上,它是一个索引值。套接字也是文件描述符。

    select函数参数介绍:

    第一个参数nfds在Linux指的是highest-numbered的文件描述符+1,类型为int。在Windows下,这个参数可以忽略,可以是任意值。

    第二个参数readfds是可选的,若不为null,select返回一个大于0的值,表示有文件可读;如果没有可读的文件,则根据timeout参数的值再判断是否超时,若超出timeout的时间,select返回0;若发生错误返回负值。

    第三个参数writefds是可选的,若不为null,select返回一个大于0的值,表示有文件可写;如果没有可写的文件,则根据timeout参数的值再判断是否超时,若超出timeout的时间,select返回0;若发生错误返回负值。

    第四个参数exceptfds是可选的,若不为null,select返回一个大于0的值,表示有异常发生在文件集合中;如果没有异常发生,则根据timeout参数的值再判断是否超时,若超出timeout的时间,select返回0;若发生错误返回负值。

    第五个参数timeout是可选的,若不为null,则用来设置超时时间,则为等待的超时时间;若为null,则将select设置为阻塞状态,直到文件描述符集合中某个文件描述符发生变化时才会返回结果。

    返回值:返回-1表示调用select函数时有错误发生,具体的错误在Linux可通过errno输出来查看,在Windows下可通过WSAGetLastError查看;返回0,表示select函数超时;返回正数即调用select函数成功,表示集合中文件描述符的数量,集合也会被修改以显示哪一个文件描述符已准备就绪。

    以下为测试代码(funset_socket.cpp):

    #include "funset.hpp"
    #ifdef _MSC_VER
    #include <WinSock2.h>
    #include <winsock.h>
    #else
    #include <sys/select.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #endif
    #include <iostream>
    
    int test_select_1()
    {
    #ifdef _MSC_VER
    	fd_set fds;
    	FD_ZERO(&fds);
    
    	timeval tv;
    	tv.tv_sec = 2;
    	tv.tv_usec = 0;
    
    	int ret = select(0, &fds, nullptr, nullptr, &tv);
    	if (ret == SOCKET_ERROR) {
    		fprintf(stderr, "fail to select, error: %d\n", WSAGetLastError());
    		return -1;
    	} else if (ret == 0) {
    		fprintf(stderr, "select timeout\n");
    		return -1;
    	} else {
    		fprintf(stdout, "success to select\n");
    	}
    #else
    	const char* path = "/dev/video0";
    	int fd = open(path, O_RDWR);
    	if (fd == -1) {
    		fprintf(stderr, "fail to open device: %s\n", path);
    	}
    
    	fd_set fds;
    	FD_ZERO(&fds);
    	FD_SET(fd, &fds);
    
    	struct timeval tv;
    	tv.tv_sec = 2;
    	tv.tv_usec = 0;
    
    	int ret = select(fd+1, &fds, nullptr, nullptr, &tv);
    	if (ret == -1) {
    		fprintf(stderr, "fail to select, error: %d, %s\n", errno, strerror(errno));
    		return -1;
    	} else if (ret == 0) {
    		fprintf(stderr, "select timeout\n");
    		return -1;
    	} else {
    		fprintf(stdout, "success to select\n");
    	}
    
    	close(fd);
    #endif
    
    	return 0;
    }

    在Linux下执行结果如下:

    GitHubhttps://github.com/fengbingchun/OpenSSL_Test

    展开全文
  • 1、select()函数返回值 select的返回值与recv函数的返回值十分类似,都分>0,=0,<0三种情况。 >0:有事件发生, FD_ISSET(socket, &fd_read); recv()....... =0:timeout,超时 <0:出错。 ...
  • select函数的阻塞和非阻塞态理解

    千次阅读 2020-06-15 16:34:58
    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct ...select函数的阻塞和非阻塞主要看最后一个参数 timeout超时时间的值,timeout的取值决定了select的三种状态: 1、timeout传...
  • 关于select函数超时时间问题

    千次阅读 2020-08-24 19:10:23
    关于select函数超时时间问题 select函数使用 int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout); 具体参数说明: 1、 int n是一个整数值,是指集合中所有文件...
  • C语言中select函数的使用

    千次阅读 2018-10-29 20:25:56
    但是使用select函数可以实现非阻塞方式的程序。它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。 Select的函数格式: int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_...
  • select函数的详细使用(C语言)

    万次阅读 2018-03-17 11:08:36
    可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,...
  • R进阶(1) --dplyr中的Select函数

    千次阅读 2020-06-23 12:21:32
    大神Hadley Wickham的dplyr包更新到了1.1.0版 。今天探究一下里面的神函数...select函数,主要用于列的选择,我们先导入含有五列的经典数据集iris和dplyr包,然后再介绍用法 > library(dplyr) > data(iris) &g
  • Oracle中select函数以及分组多表连接等重要点 函数 –函数 –内置函数和自定义函数 –单行函数:一条记录返回一个结果的 –多行函数|组函数|聚合函数:多条记录返回一条结果的 单行函数 单行函数:一条记录返回一个...
  • linux之select函数用法详解

    万次阅读 2016-01-03 10:23:27
    select系统调用是用来让我们的程序监视多个文件句柄(file ...文件句柄在Linux里很多,如果你man某个函数,在函数返回值部分说到成功后有一个文件句柄被创建的都是,如man socket可以看到“On success, a file desc
  • select函数中的坑(C语言)

    万次阅读 2019-06-28 10:17:58
    最近写了一个测试驱动的poll函数的应用程序,在应用层中调用select进行操作,设置好timeout之后,如果只对select()调用一次,就没有问题。但一旦多次调用后,就变成只有第一次timeout有效,后面的都没有效果了。 #...
  • linux select函数各参数意义

    千次阅读 2018-10-31 20:29:13
    select原型: int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);   和select模型紧密结合的四个宏: FD_CLR(int fd, fd_set *set); FD_ISSET(int fd, fd_set...
  • select函数该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它。也就是说,我们调用select告知内核对哪些描述符(就读、写或异常条件)感兴趣以及等待...
  • IO多路复用之select函数详解

    千次阅读 2016-08-17 00:48:14
    IO复用  我们首先来看看服务器编程的模型,客户端发来的请求服务端会产生一个进程来对其进行服务,每当来一... 也就是说IO复用的“介质”是进程(准确的说复用的是select和poll,因为进程也是靠调用select和poll来实
  • TCP/IP编程之select函数详解

    千次阅读 2016-07-12 00:14:11
    linux下的I/O复用模型目前很多都已经不用select函数了,而是用epoll,但是为什么还需要了解select编程呢,其实是从两个方面考虑的:一是为了通过select去理解epoll,而是非阻塞的connect函数也会用到select函数。...
  • C++网络编程Select函数用法

    千次阅读 2017-03-10 15:35:54
    可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等 待某个事件的发生...
  • select 函数的使用方法

    千次阅读 2017-08-01 09:51:28
    while(1) { FD_ZERO(&set); foreach(需要监控的文件) { fd>maxfd?...res=select(maxfd+1,&set,0,0,0); if(FD_ISSET(listen_fd,&set)) { newfd=accept(listenfd,&set); array[nsock++]=newfd; if(--res }
  • select函数详细用法解析

    万次阅读 2016-10-24 15:39:24
    int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout); 3.函数说明 select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数r
  • Linux C的select函数的使用

    万次阅读 2015-08-31 22:57:14
    1、select函数简介int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout); 当服务器响应多个客户端连接的时候,需要定义一个线程函数,在每一个线程函数里面处理该连接...
  • linux select函数详解

    万次阅读 多人点赞 2013-10-08 08:54:35
    函数实现I/O端口的复用,传递给 select 函数的参数会告诉内核:  • 我们所关心的文件描述符  • 对每个描述符,我们所关心的状态。(我们是要想从一个文件描述符中读或者写,还是关注一个描述符中是否出现异常)  ...
  • 总体上来说select函数的作用: 确定一个或多个套接口的状态,本函数用于确定一个或多个套接口的状态,对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息,用fd_set结构来表示一组等待检查的套接口,在...
  • select函数及fd_set介绍

    万次阅读 多人点赞 2016-08-03 11:30:39
    select函数及其入参的大致介绍
  • 一、select函数: #include #include #include #include int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout); 参数说明: nfds为fd_set集合中最大的文件描述符的...
  • select函数使用时应注意的问题

    千次阅读 2016-09-13 21:23:04
    select函数的使用一般分为以下几步 1、FD_ZERO(fd_set*); 2、FD_SET(int fd,fd_set*); 3、int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 4、FD_I
  • Windows Socket select函数使用

    万次阅读 2014-03-12 20:28:36
    最近做一个通信服务程序,读取数据时在工作线程中使用Socket 的select方式进行...最后认真的查了select函数的说明,才发现一些问题。 1,函数原型:  int select(int nfds, fd_set* readfds, fd_set* write

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 987,060
精华内容 394,824
关键字:

select函数