Linux网络编程--单进程服务器处理多客户请求(I/O复用)
2014-03-28 19:04
253 查看
采用I/O复用可以使用单进程的服务器去处理多个客户的连接请求,而不需要为每个客户分配单独的进程或线程去专门的处理客户请求;
I/O复用的模型:
进程阻塞于select(),而select检测相应的描述符集,如果有就绪的描述符,则退出阻塞,然后进程判断就绪的描述符并作相应的处理;
下面的例子根据前面的文章:http://blog.csdn.net/hjj414/article/details/21642183中的程序修改而来;
功能:
服务器仅仅读取客服发送的数据,并显示到屏幕上;
客户端仅仅发送数据到服务器;
服务器代码:
客户端的代码:
运行结果:
……
I/O复用的模型:
进程阻塞于select(),而select检测相应的描述符集,如果有就绪的描述符,则退出阻塞,然后进程判断就绪的描述符并作相应的处理;
下面的例子根据前面的文章:http://blog.csdn.net/hjj414/article/details/21642183中的程序修改而来;
功能:
服务器仅仅读取客服发送的数据,并显示到屏幕上;
客户端仅仅发送数据到服务器;
服务器代码:
int main(int argc, char **argv) { int listenfd; struct sockaddr_in servaddr; /*socket*/ listenfd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == listenfd) { perror("socket error."); return -1; } /*init IP address*/ bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(1234); /*set prot reuse*/ const int onflg = 1; if (-1 == setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &onflg, sizeof(onflg))) { perror("set socket prot reuse error."); return -1; } /*bind*/ if (-1 == bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) { perror("bind error."); return -1; } /*listen*/ if (-1 == listen(listenfd, 10)) { perror("listen error."); return -1; }//调用listen后建立了一个监听socket,如果没有客户连接,此时监听socket的‘已完成连接队列’为空; //accept函数将读取监听socket的已完成连接队列,如果队列为空accept将阻塞,直到队列不为空,即当 //监听socket可读的时候,accept才从退出阻塞状态;所以使用I/O复用模型,使用select函数来测试 //监听socket,当其可读时,调用accept; serv_io_multiplex(listenfd); return 0; }serv_io_multiplex:
struct cli_array_t {//记录已连接的soket描述符 int clifd[FD_SETSIZE]; //已连接的socket struct sockaddr_in *cliad[FD_SETSIZE];//客户IP地址 int maxi; //记录clifd数组中有记录的最大下标 int maxfd; //最大的描述符 }; #define BSIZE 100 int serv_io_multiplex(int listenfd) { int connfd; struct sockaddr_in cliaddr; socklen_t clilen; fd_set allset, rset; //allset为所有待检测的读描述符集,rset为当前检测的读描述符集 int nready, i; //nready记录当前描述符集中有多少个准备就绪 char buf[BSIZE]; ssize_t n; struct cli_array_t cliarr; for (i = 0; i < FD_SETSIZE; i ++) { cliarr.clifd[i] = -1; cliarr.cliad[i] = NULL; } cliarr.maxi = -1; cliarr.maxfd = listenfd; FD_ZERO(&allset); FD_SET(listenfd, &allset); //将监听socket加入allset中 while (1) { rset = allset; //更新当前检测的读描述符集 nready = select(cliarr.maxfd+1, &rset, NULL, NULL, NULL); if (-1 == nready) { perror("select error."); return -1; } /*client connected*/ if (FD_ISSET(listenfd, &rset)) {//监听socket准备就绪 clilen = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen); if (-1 == connfd) { perror("accept error."); return -1; } for (i = 0; i < FD_SETSIZE; i ++) { //记录已连接socket,并记录客户IP if (cliarr.clifd[i] < 0) { cliarr.clifd[i] = connfd; cliarr.cliad[i] = malloc(sizeof(cliaddr)); if (NULL == cliarr.cliad[i]) { printf("malloc error.\n"); return -1; } cliarr.cliad[i]->sin_addr = cliaddr.sin_addr; cliarr.cliad[i]->sin_port = cliaddr.sin_port; break; } } FD_SET(connfd, &allset); //将已连接的socket描述符加入allset描述符集中 if (i > cliarr.maxi) cliarr.maxi = i; if (connfd > cliarr.maxfd) cliarr.maxfd = connfd; if (--nready <= 0) { //如果没有其他的可读就绪,进入下一次检测 continue; } } /*read clients*/ for (i = 0; i <= cliarr.maxi; i ++) { if (cliarr.clifd[i] < 0){ continue; } if (FD_ISSET(cliarr.clifd[i], &rset)) {// 已连接的socket读准备就绪 if (0 == (n=read(cliarr.clifd[i], buf, BSIZE))) { //客户输入ctrl+D或者ctrl+C,即客户关闭 /*client close the socket*/ close(cliarr.clifd[i]); FD_CLR(cliarr.clifd[i], &allset); cliarr.clifd[i] = -1; printf("<IP:%s @PORT:%d exit>\n", inet_ntoa(cliarr.cliad[i]->sin_addr), \ ntohs(cliarr.cliad[i]->sin_port), buf); free(cliarr.cliad[i]); cliarr.cliad[i] = NULL; } else { //显示读取到的信息 buf = '\0'; printf("IP:%s @PORT:%d# %s", inet_ntoa(cliarr.cliad[i]->sin_addr), \ ntohs(cliarr.cliad[i]->sin_port), buf); } if (--nready <= 0) //如果没有已连接的socket可读,则进入下一次检测 break; } } while (cliarr.clifd[cliarr.maxi]<0 && cliarr.maxi>=0) cliarr.maxi --; } }
客户端的代码:
int main(int argc, char **argv) { int sockfd; struct sockaddr_in servaddr; if (2 != argc) { printf("usage: ./client 127.0.0.1"); return -1; } /*socket*/ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == sockfd) { perror("socket error."); return -1; } /*connect*/ bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(1234); inet_pton(AF_INET, argv[1], &servaddr.sin_addr); if (-1 == connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) { perror("connect error."); return -1; } str_cli(stdin, sockfd); return 0; } #define LSIZE 100 void str_cli(FILE *fp, int sockfd) //仅仅发送数据到服务器 { char sendline[LSIZE], recvline[LSIZE]; while (NULL != fgets(sendline, LSIZE, fp)) { n_write(sockfd, sendline, strlen(sendline)); } }
运行结果:
……
相关文章推荐
- Linux TCP server系列(1)-简单TCP服务器+多进程处理客户请求
- Linux TCP server系列(1)-简单TCP服务器+多进程处理客户请求
- 用TCP/TP进行网际互连(6) ———— 利用I/O复用完成单进程并发服务器的处理
- 客户分端--服务器 请求处理模型总结
- Nginx 之三:nginx服务器模块、web请求处理机制及事件驱动模型、进程功能和进程间通信
- ORACLE服务器进程处理用户进程请求的全过程
- Nginx 之三:nginx服务器模块、web请求处理机制及事件驱动模型、进程功能和进程间通信
- UNIX网络编程卷1 服务器程序设计范式1 并发服务器,为每个客户请求fork一个进程
- 高性能Web服务器Nginx的配置与部署研究(3)Nginx请求处理机制
- 连接的用户过多 导致此错误的原因是:Web 服务器忙,因通信量过大而无法处理您的请求。
- 你想建设一个能承受500万PV/每天的网站吗?服务器每秒要处理多少个请求才能应对?
- 自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求
- ASP.NET服务器对于请求的处理过程
- 邮箱系统服务器CLOSE_WAIT状态连接进程不释放的处理
- 【转】ASP.NET服务器对于请求的处理过程
- 服务器网络模型(2)---服务器的请求处理流程
- SharePoint 2010 之soap:Server服务器无法处理请求
- 第十九篇:不为客户连接创建子进程的并发回射服务器(select实现)
- 表服务器无法打开与报表服务器数据库的连接。所有请求和处理都要求与数据库建立连接。
- 由于内部错误,服务器无法处理该请求。有关该错误的详细信息,请打开服务器上的 IncludeExceptionDetailInFaults (从 ServiceBehaviorAttribute 或从 <serviceDebug> 配置行为)以便将异常信息发送回客户端,