從實踐之中,我又學到東西了!使用select的時候,不管是使用非阻塞仍是阻塞socket,調用recv和send函數返回0都意味着socket被遠程關閉!!! html
select+阻塞socket版本請見 http://xiaoxia.org/2675.html 服務器
對比兩個版本,從理論上能夠知道select+非阻塞socket要高效得多。在與遠程服務器connect的時候,程序要等待鏈接徹底創建完畢才返回,這裏會讓程序產生延遲。而在非阻塞中不會等待,直接去處理其它鏈接。因此,我打算在fox3(http://code.google.com/p/icefox)的開發中使用select+非阻塞socket。至於爲什麼不使用epoll或者iocp是由於我考慮了可移植性。 網絡
#include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netdb.h> #include <errno.h> #include <fcntl.h> #define closesocket close #define client_count 100 #define BUFFER_SIZE 8192 struct connection{ int clientfd; int remotefd; char clientbuf[BUFFER_SIZE]; char remotebuf[BUFFER_SIZE]; int clientbuf_size; int remotebuf_size; } conns[client_count] = {0}; static void remove_client(int i) { shutdown(conns[i].clientfd, SHUT_RD); closesocket(conns[i].clientfd); shutdown(conns[i].remotefd, SHUT_RD); closesocket(conns[i].remotefd); conns[i].clientfd = conns[i].remotefd = 0; } static int get_client() { int i; for( i = 0; i<client_count; i++) if(!conns[i].clientfd) return i; return 0; } static int set_nonblocking(int sock) { int opts; opts = fcntl(sock, F_GETFL); if(opts < 0) return -1; opts = opts | O_NONBLOCK; if(fcntl(sock, F_SETFL, opts) < 0) return(-1); return 0; } int main(int argc, char **argv) { fd_set fdreads, fdwrites; const int buf_size = 1024*32; int ret; char buf[buf_size]; int sock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); ret = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&ret, sizeof(ret)); //端口複用 struct sockaddr_in addr = {0}; struct sockaddr_in remote_addr = {0}; addr.sin_family = remote_addr.sin_family = PF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons( 1080 ); remote_addr.sin_addr.s_addr = inet_addr("221.130.162.247"); remote_addr.sin_port = htons( 80 ); if( bind( sock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in) ) < 0 ) perror("failed to bind socket"); listen( sock , 5); printf("listening\n"); for(;;){ int i, j, k, lastfd=0; FD_ZERO(&fdreads); FD_ZERO(&fdwrites); FD_SET( sock, &fdreads); for( i=0; i<client_count; i++) if(conns[i].clientfd){ if( conns[i].clientbuf_size == 0 ){ FD_SET( conns[i].clientfd, &fdreads ); }else{ FD_SET( conns[i].remotefd, &fdwrites ); } if( conns[i].remotebuf_size == 0){ FD_SET( conns[i].remotefd, &fdreads ); }else{ FD_SET( conns[i].clientfd, &fdwrites ); } } ret = select(client_count*2+2, &fdreads, &fdwrites, 0, 0); switch(ret){ case -1: perror("error"); break; case 0: perror("timeout?"); break; default: if(FD_ISSET(sock, &fdreads)){ int j = get_client(); printf("accept %d\n", j); int len = sizeof(struct sockaddr_in); conns[j].clientfd = accept(sock, 0, 0); conns[j].remotefd = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); addr.sin_port = htons( 0 ); set_nonblocking(conns[j].clientfd); set_nonblocking(conns[j].remotefd); bind( conns[j].remotefd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in) ); connect( conns[j].remotefd, (struct sockaddr*)&remote_addr, sizeof(struct sockaddr_in) ); } for( j=0; j<client_count; j++){ if(!conns[j].clientfd) continue; if(FD_ISSET(conns[j].clientfd, &fdreads) ){ int ret = recv(conns[j].clientfd, conns[j].clientbuf, BUFFER_SIZE, 0); if( ret > 0 ) conns[j].clientbuf_size = ret; if( ret <= 0 ) remove_client(j); } if(FD_ISSET(conns[j].remotefd, &fdreads) ){ int ret = recv(conns[j].remotefd, conns[j].remotebuf, BUFFER_SIZE, 0); if( ret > 0 ) conns[j].remotebuf_size = ret; if( ret <= 0 ) remove_client(j); } if(FD_ISSET(conns[j].clientfd, &fdwrites) ){ int ret = send(conns[j].clientfd, conns[j].remotebuf, conns[j].remotebuf_size, 0); if( ret > 0 ) conns[j].remotebuf_size -= ret; if( ret <= 0 ) remove_client(j); } if(FD_ISSET(conns[j].remotefd, &fdwrites) ){ int ret = send(conns[j].remotefd, conns[j].clientbuf, conns[j].clientbuf_size, 0); if( ret > 0 ) conns[j].clientbuf_size -= ret; if( ret <= 0 ) remove_client(j); } } } } return 0; }