zoukankan      html  css  js  c++  java
  • select函数的介绍和使用

    我们所使用的I/O模型一共有五种。

    分别为阻塞I/O,非阻塞I/O,I/O复用,信号驱动I/O,异步I/O。

    所谓I/O复用就是指管理多个I/O文件描述符,一般会使用(select,poll,epoll)3个函数中的一个来实现I/O复用。

    下面就介绍一下其中的select

    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的5个参数:

    第一个参数是当前管理的所有文件描述符的最大值加1

    因为在select中采用的是轮询的机制,输入参数是不包含的。

    你可以这样理解:   for(fd = 0; fd<maxfd+1 ; fd++)

    [顺带一提: 文件描述符0是stdin,1是stdout,2是stderr]

    第二个参数是读集合

    你要管理哪些文件描述符的读的事件,就把它放到fd_set *readfds结构体中(用FD_SET()函数);

    第三个参数是写集合

    同样,要管理哪些文件描述符的写的事件,就把它放到fd_set *readfds结构体中(用FD_SET()函数);

    第四个参数是异常集合

    就是监视异常事件的发生

    第五个参数是超时时间的设置

    struct timeval {

     long tv_sec; 秒

     long tv_usec; 毫毛
    }

    不要忽略这也是一个输入输出参数(即该参数会被函数改变),如果是一个循环,那么每次都要重新赋值该参数。

    注意:select中除了第一个参数都是输入输出参数,即放在这几个位置的值都会被改变

    读,写,异常集合输入参数是要监视的集合,使用完select后会变成有事件发生的事件集合,而timeout会被select函数减少

    第1个参数必须设置正确(在window系统中可以不设置,但在linux系统中必须设置)

    第2,3,4个参数若设置为NULL,表示不关心任何事件,若不关心其中的一种事件,可将其设置为NULL。

    顺带提一下[int fileno(FILE *stream);],这个函数可以用来取得参数stream指定的文件流所使用的文件描述符。

    用select函数实现并发服务器并发数量受两方面限制

    1.最大文件描述符(系统参数)

    可在LINUX终端直接输入 ulimit -n 查看,用 ulimit -n [数值] 改变参数

    或者在程序中用setrlimit 函数改变(注意:setrlimit 函数只能改变当前进程的最大文件描述符)

    2.select中的fd_set集合容量FD_SETSIZE,不容易改变,需要重新编译内核

    下面是select实现的(可实现多个客户端连接)回射服务器代码

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <sys/socket.h>
      4 #include <string.h>
      5 #include <sys/types.h>
      6 #include <unistd.h>
      7 #include <errno.h>
      8 #include <arpa/inet.h>
      9 #include <netinet/in.h>
     10 #include <sys/select.h>
     11 #include <sys/time.h>
     12 #include <sys/types.h>
     13 
     14 
     15 void myerr(char *m)
     16 {
     17     perror(m);
     18     exit(0);
     19 }
     20 
     21 int count = 0;
     22 int main()
     23 {
     24     int listenfd;
     25     if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
     26         myerr("socket error");
     27 
     28     int on = 1;
     29         if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
     30                 myerr("setsockopt error");
     31 
     32     
     33     struct sockaddr_in servaddr;
     34     memset(&servaddr, 0, sizeof(servaddr));
     35     servaddr.sin_port = htons(5188);
     36     servaddr.sin_family = AF_INET;
     37     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
     38     
     39     if(bind(listenfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) < 0)
     40         myerr("bind error");
     41     
     42     if(listen(listenfd, SOMAXCONN) < 0)
     43         myerr("listen error");
     44     
     45     int i, maxi=0, conn, ret, maxfd, nready;
     46     char recvbuf[1024] = "";
     47     struct sockaddr_in peeraddr;
     48     int connfd[FD_SETSIZE];
     49     for(i=0; i<FD_SETSIZE; i++)
     50         connfd[i] = -1;
     51     socklen_t peerlen = sizeof(peeraddr);
     52 
     53     maxfd = listenfd;
     54     fd_set allset, rset;
     55     FD_ZERO(&allset);
     56     FD_ZERO(&rset);
     57     FD_SET(listenfd, &allset);
     58 
     59     for(;;)
     60     {
     61         rset = allset;
     62         sleep(1);
     63         nready = select(maxfd+1, &rset, NULL, NULL, NULL);
     64         if(nready == -1)
     65         {
     66             if(errno == EINTR)
     67                 continue;
     68             myerr("select");
     69         }
     70         if(FD_ISSET(listenfd, &rset))
     71         {
     72             if((conn = accept(listenfd, (struct sockaddr *)&peeraddr, &peerlen)) < 0)
     73                 myerr("accept error");
     74             for(i=0; i<FD_SETSIZE; i++)
     75             {
     76                 if(connfd[i] == -1)
     77                 {
     78                     connfd[i] = conn;
     79                     if(i > maxi)
     80                         maxi = i;
     81                     break;
     82                 }
     83             }
     84         //    count++;
     85         //    printf("%d
    ", count);
     86             if(i == FD_SETSIZE)
     87             {
     88                 fprintf(stderr, "too many clients
    ");
     89                 exit(1);
     90             }
     91             printf("ip: %s  port: %d
    ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
     92             FD_SET(conn, &allset);
     93             if(conn > maxfd)
     94                 maxfd = conn;
     95             if(--nready <= 0)
     96                 continue;
     97         }
     98         for(i=0; i<=maxi; i++)
     99         {
    100             if(connfd[i] == -1)
    101                 continue;
    102             conn = connfd[i];
    103 
    104             if(FD_ISSET(conn, &rset))
    105             {
    106                 memset(recvbuf, 0, sizeof(recvbuf));
    107                 ret = read(conn, recvbuf, sizeof(recvbuf));
    108                 if(ret == 0)
    109                 {
    110                     printf("client close
    ");
    111                     close(conn);
    112                     connfd[i] = -1;
    113                     while(connfd[maxi] == -1)
    114                     {
    115                         maxi --;
    116                         if(maxi == 0)
    117                             break;
    118                     }
    119                     FD_CLR(conn, &allset);
    120                     continue;
    121                 }
    122                 write(conn, recvbuf, ret);
    123                 fputs(recvbuf, stdout);
    124                 if(--nready <= 0)
    125                     break;
    126             }
    127         }
    128     }
    129     close(listenfd);
    130     return 0;
    131 }
  • 相关阅读:
    那些年读过的书《Java并发编程实战》和《Java并发编程的艺术》三、任务执行框架—Executor框架小结
    那些年读过的书《Java并发编程实战》二、如何设计线程安全类
    那些年读过的书《Java并发编程实战》一、构建线程安全类和并发应用程序的基础
    重读《深入理解Java虚拟机》七、探究Java内存模型
    重读《深入理解Java虚拟机》六、Java泛型 VS C#泛型 (伪泛型 VS 真泛型)
    重读《深入理解Java虚拟机》五、虚拟机如何执行字节码?程序方法如何被执行?虚拟机执行引擎的工作机制
    重读《深入理解Java虚拟机》四、虚拟机如何加载Class文件
    input 文本框禁止输入表情
    win7右下角无线网图标显示未连接,但是实际上已连接上,也能上网
    history.back(-1)和history.go(-1)的区别
  • 原文地址:https://www.cnblogs.com/kamicoder/p/6418034.html
Copyright © 2011-2022 走看看