服务器编程入门(11)TCP并发回射服务器实现 - 单线程select实现
- 单进程,IO复用,实现多个连接同时监听和收发信息
- 当服务器进程一终止,客户就能马上得到结果(select +shutdown实现)
- 当客户端使用"exit"命令或者Cirl+C结束进程时,服务器可以立即感应到,并关闭当前接口(select+close实现
- 服务器连接了第一个客户,并收发消息“hello world”
- 服务器连接了第二个客户,并收发消息“hello select”
- 服务器从第一个客户收发消息“hello world again”
- 服务器从第二个客户收发消息“hello select again”
- 第二个客户关闭连接
- 第一个客户关闭连接
在了解select实现之前,先复习一下之前了解的IO模型
- 阻塞式IO
- 非阻塞式IO
- IO复用
- 信号驱动式IO
- 异步IO
- 等待数据准备好
- 从内核向进程复制数据
- 等待数据从网络中到达,当所等待分组到达时,它被复制到内核中的某个缓冲区
- 把数据从内核缓冲区复制到应用进程缓冲区
- select主要通过维护两个数组,来实现端口的轮询:
- client[]数组,记录有哪些连接已经建立
- rset[]数组,记录有注册哪些端口,需要监听
- 当rset数组中注册的端口被激活,这时将端口号放到client数组中,稍后遍历client[]数组,处理连接上的数据
#include "mtserver.h" int main(int argc, char* argv[]) { checkArgc(argc, 2); const char* ip = argv[1]; int port = atoi( argv[2] ); /* declare socket*/ int listenfd, connfd, sockfd; int ret; /* initialize listen socket*/ mySocket(listenfd); /* server address */ struct sockaddr_in servaddr; initSockAddr(servaddr, ip, port); /* bind */ myBind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)); /* listen */ myListen(listenfd, 5); /* handle SIGCHLD signal*/ //signal(SIGCHLD, handle_sigchild); /* waiting for connecting */ pid_t chipid; socklen_t clilen; struct sockaddr_in cliaddr; /* select initialize */ int maxfd, maxi, i; bool toclose; int nready, client[FD_SETSIZE]; fd_set rset, allset; maxfd = listenfd; maxi = -1; for ( i=0; i < FD_SETSIZE; i++ ) client[i] = -1; FD_ZERO(&allset); FD_SET(listenfd, &allset); printf("Waiting for connecting...\n"); for(;;) { rset = allset; if ( (nready=select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 ) { fprintf(stderr, "select failed.%s\n", strerror(errno)); continue; } /* handle listen fd and no recv or respond */ if (FD_ISSET(listenfd, &rset)) { clilen = sizeof(cliaddr); connfd = myAccept(listenfd, (struct sockaddr*)&cliaddr, &clilen); printf("Connection is established with sockfd: %d\n", connfd); for ( i = 0; i < FD_SETSIZE; i++) { if ( client[i] < 0 ) { client[i] = connfd; break; } } if (i == FD_SETSIZE) { fprintf(stderr, "too many clients\n" ); break; } FD_SET( connfd, &allset ); if ( connfd > maxfd ) { maxfd = connfd; } if ( i > maxi) { maxi = i; } if (--nready <= 0) { continue; } } /* handle accept fds(client[]) and handle recv or respond msg */ for ( i = 0; i <= maxi; i++) { if ( (sockfd = client[i]) < 0 ) continue; if ( FD_ISSET(sockfd, &rset) ) { if( (toclose = handle_recv(sockfd))) { printf("Client close this connection: %d\n" , sockfd); close(sockfd); FD_CLR(sockfd, &allset); client[i] = -1; } if (--nready <= 0) break; } } } } bool handle_recv(int connfd) { char recvbuf[BUFSIZE]; memset( recvbuf, ‘\0‘, BUFSIZE ); if ( recv(connfd, recvbuf,BUFSIZE,0) != 0) { if (!strcmp(recvbuf, "exit")) return true; fprintf(stderr,"recv msg: \"%s\" from connfd:%d\n", recvbuf, connfd); send(connfd, recvbuf, strlen(recvbuf), 0); fprintf(stderr,"send back: \"%s\" to connfd:%d\n\n", recvbuf, connfd); } else return true; return false; }
#include "mtclient.h" int main(int argc, char* argv[]) { checkArgc(argc, 2); int port = atoi(argv[2]); char* ip = argv[1]; int sockfd; struct sockaddr_in servaddr; mySocket(sockfd); initSockAddr(servaddr,ip, port); myConnect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)); handle_msg(sockfd); exit(0); } void handle_msg(int sockfd) { char sendbuf[BUFSIZE]; char recvbuf[BUFSIZE]; int maxfdpl, ret; fd_set rset; int normalTermi = 0; FD_ZERO(&rset); while(1) { memset( sendbuf, ‘\0‘, BUFSIZE ); memset( recvbuf, ‘\0‘, BUFSIZE ); if (normalTermi == 0) FD_SET( 0, &rset ); FD_SET( sockfd, &rset ); maxfdpl = sockfd + 1; if(DEBUG) printf("Debug: waiting in select\n"); if ( select( maxfdpl, &rset, NULL, NULL, NULL) < 0 ) { fprintf(stderr, "select failed.%s\n", strerror(errno)); } if(DEBUG) printf("Debug: after select\n"); if (FD_ISSET( sockfd, &rset )) { if (recv(sockfd, recvbuf, BUFSIZE, 0) == 0) { if(DEBUG) printf("Debug: ready to quit, normalTermi: %d\n" , normalTermi); if (normalTermi == 1) { printf("handle_msg: normal terminated.\n"); return; } else { printf("handle_msg: server terminated.\n"); exit(0); } } fprintf(stderr, "recv back: %s\n", recvbuf); } else if ( FD_ISSET( 0, &rset ) ) { gets(sendbuf); if (strlen(sendbuf) > 0) { send(sockfd, sendbuf, strlen(sendbuf), 0); if ( !strcmp(sendbuf, "exit") ) { normalTermi = 1; shutdown(sockfd, SHUT_WR); FD_CLR(0, &rset); continue; } } } } close( sockfd ); return; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。