io多路复用是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般都是读就绪或者写就绪),就能通知应用程序进行相应的读写操作。select函数作为io多路复用的机制,第一个参数nfds是fd_set集合中最大描述符值+1,fdset是一个位数组,每一位代表其对应的描述符是否需要被检查。第二三四参数表示需要关注读、写、错误时间的文件描述符位数组,这些参数既是输入型参数也是输出型参数,可能会被内核修改用于标识哪些描述符上发生了关注的事件,所以每次调用select前都需要重新

  • 摘要:io多路复用是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般都是读就绪或者写就绪),就能通知应用程序进行相应的读写操作。select函数作为io多路复用的机制,第一个参数nfds是fd_set集合中最大描述符值+1,fdset是一个位数组,每一位代表其对应的描述符是否需要被检查。第二三四参数表示需要关注读、写、错误时间的文件描述符位数组,这些参数既是输入型参数也是输出型参数,可能会被内核修改用于标识哪些描述符上发生了关注的事件,所以每次调用select前都需要重新
  • io多路复用是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般都是读就绪或者写就绪),就能通知应用程序进行相应的读写操作。select函数作为io多路复用的机制,第一个参数nfds是fd_set集合中最大描述符值+1,fdset是一个位数组,每一位代表其对应的描述符是否需要被检查。第二三四参数表示需要关注读、写、错误时间的文件描述符位数组,这些参数既是输入型参数也是输出型参数,可能会被内核修改用于标识哪些描述符上发生了关注的事件,所以每次调用select前都需要重新初始化fdset。timeout参数为超时时间,该结构会被内核修改,其值为超时剩余时间。

    select函数返回值有三种,返回-1时表示select失败;返回0表示超时;其他返回值表示select成功。

  •  2018-12-02 18:56:42

  1 #include<stdio.h>
  2 #include<sys/socket.h>
  3 #include<sys/types.h>
  4 #include<stdlib.h>
  5 #include<netinet/in.h>
  6 #include<arpa/inet.h>
  7 #include<assert.h>
  8 int rfds[128];
  9 void usage(const char* proc)
 10 {
 11     assert(proc);
 12     printf("usage:%s: [ip] [port]/n",proc);
 13 }
 14 int start_up(const char* ip,int port)
 15 { 
 16     assert(ip);
 17     assert(port > 0);
 18     int sock = socket(AF_INET,SOCK_STREAM,0); 
 19     if(sock < 0) 
 20     { 
 21         perror("socket");
 22         exit(2); 
 23     } 
 24     struct sockaddr_in local; 
 25     local.sin_family = AF_INET; 
 26     local.sin_port = htons(port); 
 27     local.sin_addr.s_addr =inet_addr(ip);
 28     if(bind(sock,(struct sockaddr*)&;local,sizeof(local)) < 0)
 29     { 
 30         perror("bind");
 31         exit(3); 
 32     }
 33  
 34     if(listen(sock,5) < 0) 
 35     {
 36         perror("listen"); 
 37         exit(4); 
 38     } 
 39     return sock;
 40     }
 41 int main(int argc,char* argv[])
 42 { 
 43     if(argc != 3) 
 44     { 
 45         usage(argv[0]);
 46         return 1;
 47     } 
 48     int listen_sock = start_up(argv[1],atoi(argv[2]));
 49     for(int i=0 ;i < 128; i++) 
 50     {
 51         rfds[i] = -1;
 52     } 
 53     fd_set rset; 
 54     int max_fd = 0; 
 55     while(1) 
 56     {
 57         struct timeval timeout = {0,0};
 58         FD_ZERO(&;rset);
 59         rfds[0] = listen_sock;
 60         max_fd = listen_sock; 
 61         for(int i=0 ;i < 128; i++) 
 62         { 
 63             if(rfds[i] >= 0) 
 64             {
 65                 FD_SET(rfds[i],&;rset);
 66                 if(max_fd < rfds[i]) 
 67                     max_fd = rfds[i]; 
 68             } 
 69         }
 70         switch(select(max_fd + 1,&;rset,NULL,NULL,NULL))
 71         { 
 72         case -1: 
 73                 perror("select");
 74                 break; 
 75         case 0: 
 76                 printf("timeout");
 77                 break;            
 78         default: 
 79         { 
 80             int j = 0;
 81             for(;j < 128;j++) 
 82             { 
 83                 if(rfds[j] < 0)
 84                     continue; 
 85                 if(j == 0&;&;FD_ISSET(rfds[j],&;rset))
 86                 { 
 87                     struct sockaddr_in client; 
 88                     socklen_t len = sizeof(client); 
 89                     int new_fd = accept(listen_sock,(struct sockaddr*)&;client,&;len); 
 90                     if(new_fd < 0) 
 91                     {
 92                         perror("accept");
 93                     } 
 94                     else 
 95                     { 
 96                         printf("get a client:socket :%s:%d/n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
 97                         int k = 0; 
 98                         for(;k < 128;k++)
 99                         { 
100                             if(rfds[k] == -1) 
101                             { 
102                                 rfds[k] = new_fd; break;
103                             } 
104                         } 
105                         if( k == 128)
106                         {
107                             close(new_fd); 
108                         } 
109                     } 
110                 } 
111                 else if(FD_ISSET(rfds[j],&;rset)) 
112                 { 
113                     char buf[1024]; 
114                     ssize_t s = read(rfds[j],buf,sizeof(buf)-1);
115                     if(s > 0)
116                     {
117                         buf[s] = 0;
118                         printf("client#%s/n",buf);
119                     }
120                     else if(s == 0) 
121                     { 
122                         printf("client close.../n");
123                         close(rfds[j]); rfds[j] = -1; 
124                     } 
125                     else 
126                     { 
127                         perror("read"); 
128                     } 
129                 } 
130             }
131         } 
132         break;
133         }
134     }
135 }

select_server.c

使用telnet为一个客户端访问:

 

使用select编写服务器

当把服务器终止后,再次打开服务器会出现这种情况:

使用select编写服务器

这是因为,虽然server的应用程序终止了,但tcp协议层的连接没有完全断开,因此不能再次监听同样的server端口。

client终止时自动关闭socket描述符,server的TCP连接收到client发的FIN段后处于TIME_WAIT状态。TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态,因为我们先Ctrl-C终止了server,所以server是主动关闭连接的一方,在TIME_WAIT期间仍然不能再次监听同样的server端口。MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,在Linux上一般经过半分钟后 就可以再次启动server了。

版权声明:本文为fm98原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/MessiXiaoMo3334/p/10054617.html