io复用select方法编写的服务器
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为一个客户端访问:
当把服务器终止后,再次打开服务器会出现这种情况:
这是因为,虽然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了。