从实践之中,我又学到东西了!使用select的时候,无论是使用非阻塞还是阻塞socket,调用recv和send函数返回0都意味着socket被远程关闭!!!
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; }
真历害~~
在学linux下的网络编程~
我看你的功底好深,你功底好学什么都快
真爽~
有些时候有些方面和功底没太多关系
Try EPOLL.
@Heiher:谢谢!我之前尝试过,不过好像还没有学会。。。 如果对性能要求不是很苛刻,为了便于跨平台,我现在还是优先使用select。
coding风格挺好,学习了!
找了好久的东西,真的很感谢