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