Linux TCP server系列(5)-select模式下的单进程server
目标:
让服务器退化为单进程模式,但是利用select来提升性能
思路:
(1)服务器
传统的单进程服务器一旦accept了客户端的TCP连接后,就转入客户请求的处理,处理完成后才能再一次的调用accept来接受下一个客户端的TCP连接和请求。
为了更加提高单进程server的性能,本程序使用select这种IO复用的模式,同时监听已经连接的socket端口和正在监听的服务器listening端口,这样一来,就可以大大提升sever处理并发请求的能力。
select的使用方式如下:
a)定义fd_set
fd_set allset;
select允许我们监听来自标准输入,标准输出,标准错误输出的IO信号,本例中我们监听标准输入IO信号集
b)注册将要被监听的fd
FD_SET( listenfd, &allset )
通过FD_SET和 FD_CLR可以注册和清除某个fd_set内的fd项,使得在调用select的时候可以监听或者取消监听某个fd
c)如果IO信号到达,识别并处理
通过FD_ISSET可以判断select所监听的fd_set上的IO是否有状态变化,一旦返回true,则可以对该fd进行操作。
select使用事项及技巧:
a)使用select时应该注意,如果select有timeout设置,那么每次select之前都要再重新设置一下timeout的值,因为select成功的话会修改timeout的值。
b)本例中,如果我们在某次select中捕获到listenfd的IO状态有变,也就是说有新的客户端连接,我们不会马上做客户端的请求处理,而是把连接到的socket fd插入到select的监听集合中,然后继续探测其他监听集有IO状态变化(这里的其他监听集就是每个已经连接的客户端的socket fd的状态),如果有变化则马上处理client的请求。这样做的好处是我们及时处理了已连接的客户端的请求,而不是被新连接的客户端的请求所抢占,反正旧客户端被饿死的情况发生。
c) 本例是在单进程服务器上使用select,所以适合简单客户请求处理,也就是短连接的情况,如果需要长时间服务于多个客户,可以使用fork加以辅助
(2)客户端无需改动
代码:
server.cpp
1 #include<sys/types.h> 2 #include<sys/socket.h> 3 #include<strings.h> 4 #include<arpa/inet.h> 5 #include<unistd.h> 6 #include<stdlib.h> 7 #include<stdio.h> 8 #include<string.h> 9 #include<errno.h> 10 #include<signal.h> 11 #include<sys/wait.h> 12 #include<pthread.h> 13 14 #define LISTEN_PORT 84 15 16 void str_echo(int sockfd) // 服务器收到客户端的消息后的响应 17 { 18 ssize_t n; 19 char line[512]; 20 21 printf("ready to read/n"); 22 23 while( (n=read(sockfd,line,512))>0 ) 24 { 25 line[n]='/0'; 26 printf("Client Diary: %s/n",line); 27 28 char msgBack[512]; 29 snprintf(msgBack,sizeof(msgBack),"recv: %s/n",line); 30 write(sockfd,msgBack,strlen(msgBack)); 31 bzero(&line,sizeof(line)); 32 } 33 34 printf("end read/n"); 35 36 } 37 38 void sig_child(int signo) //父进程对子进程结束的信号处理 39 { 40 pid_t pid; 41 int stat; 42 43 while( (pid=waitpid(-1,&stat,WNOHANG))>0) 44 printf("child %d terminated/n",pid); 45 46 return; 47 } 48 49 50 int main(int argc, char **argv) 51 { 52 53 int listenfd, connfd; 54 pid_t childpid; 55 socklen_t chilen; 56 57 struct sockaddr_in chiaddr,servaddr; 58 59 //values for select 60 int i,maxi,maxfd,sockfd; 61 int nready,client[FD_SETSIZE]; 62 ssize_t n; 63 fd_set rset,allset; 64 //values for select 65 66 listenfd=socket(AF_INET,SOCK_STREAM,0); 67 if(listenfd==-1) 68 { 69 printf("socket established error: %s/n",(char*)strerror(errno)); 70 } 71 72 bzero(&servaddr,sizeof(servaddr)); 73 servaddr.sin_family=AF_INET; 74 servaddr.sin_addr.s_addr=htonl(INADDR_ANY); 75 servaddr.sin_port=htons(LISTEN_PORT); 76 77 int bindc=bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)); 78 if(bindc==-1) 79 { 80 printf("bind error: %s/n",strerror(errno)); 81 } 82 83 listen(listenfd,SOMAXCONN); //limit是SOMAXCONN 84 85 //initial "select" elements 86 maxfd=listenfd; //新增listenfd,所以更新当前的最大fd 87 maxi=-1; 88 for(i=0;i<FD_SETSIZE;i++) 89 client[i] = -1; 90 FD_ZERO(&allset); 91 FD_SET(listenfd,&allset); 92 //initial "select" elements 93 94 signal(SIGCHLD,sig_child); 95 for(;;) 96 { 97 rset=allset; //rset和allset的搭配使得新加入的fd要等到下次select才会被监听 98 nready=select(maxfd+1,&rset,NULL,NULL,NULL); //一开始select监听的是监听口 99 //如果有timeout设置,那么每次select之前都要再重新设置一下timeout的值 100 //因为select会修改timeout的值。 101 102 if(FD_ISSET(listenfd,&rset)) 103 { 104 chilen=sizeof(chiaddr); 105 106 connfd=accept(listenfd,(struct sockaddr*)&chiaddr,&chilen); 107 //阻塞在accept,直到三次握手成功了才返回 108 if(connfd==-1) 109 printf("accept client error: %s/n",strerror(errno)); 110 else 111 printf("client connected/n"); 112 113 for(i=0;i<FD_SETSIZE;i++) 114 { 115 if (client[i]<0) 116 { 117 client[i]=connfd; //找一个最小的插进入,并且缓存在client中,这样就不需要遍历所有fd,包括为0位的,来查看是否ISSET 118 break; 119 } 120 } 121 if(i==FD_SETSIZE) 122 { 123 printf("too many clients/n"); 124 exit(0); 125 } 126 FD_SET(connfd,&allset); //新加入的描述符,还没判断是否可以或者写,所以后面使用rset而不是allset 127 128 if(connfd>maxfd) //maxfd是为了下次select,作为参数使用 129 maxfd=connfd; 130 if(i>maxi) //maxi是为了减少遍历所监听fd的次数 131 maxi=i; 132 if(--nready<=0) //nready用来辅助计数,这样就不要遍历整个client数组 133 continue; 134 } 135 136 137 for(i=0;i<=maxi;i++) 138 { 139 if( (sockfd=client[i]) <0) 140 continue; 141 if(FD_ISSET(sockfd,&rset)) 142 { 143 //单进程的环境下,不可以阻塞在这里,可以选择非阻塞,线程,超时.也就无法防范拒绝服务的攻击 144 //比较适合短连接的情况 145 146 //单进程不使用fork的情况! 147 //test fork 148 // if((childpid=fork())==0) 149 { 150 close(listenfd); 151 printf("client from %s/n",inet_ntoa(chiaddr.sin_addr)); 152 str_echo(connfd); 153 close(connfd); 154 155 exit(0); 156 } 157 // else if (childpid<0) 158 // printf("fork error: %s/n",strerror(errno)); 159 close(connfd); 160 //test fork 161 162 FD_CLR(sockfd,&allset); //清除,表示已被处理 163 client[i]=-1; 164 165 printf("can read : %d,%d,%d/n",i,sockfd,nready); 166 if(--nready<=0) //nready用来辅助计数,这样就不要遍历整个client数组 167 break; 168 } 169 } 170 } 171 }
作者: Aga.J
出处: http://www.cnblogs.com/aga-j
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
Linux TCP server系列(5)-select模式下的单进程server相关推荐
- Linux TCP server系列(6)-select模式下的多线程server
目标: 修改上一篇的select模式下的server,让它使用多线程来处理客户端请求(多进程的模式已经在上篇中加了注释). 思路: (1)服务器 我们已经在之前的客户端模型多个并发用户的过程中使用过多 ...
- 在单用户模式下启动SQL Server的不同方法
In this article, we will review different ways to start SQL Server in single user mode. 在本文中,我们将介绍在单 ...
- js监听select值变化_网络编程——C++实现socket通信(TCP)高并发之select模式
相关函数: 服务端: socket() bind() listen() FD_ZERO()等辅助函数 select() 高并发select模式 accept() read() 或 recv()等 wr ...
- linux efi 双系统,EFI+GPT模式下Linux与Windows双系统要诀
本文并非要对 BIOS/EFI/MBR/GPT 等进行理论探讨,相关知识请各位自行搜索学习.本着薄荷网一贯坚持的实操原则,本文主要是介绍在"EFI引导+GPT分区"模式下,安装 L ...
- linux 返回非法指令,linux – ARM Cortex A7在内核模式下返回PMCCNTR = 0,在用户模式下返回非法指令(即使在PMUSERENR = 1之后)...
我想在Raspberry Pi 2上读取循环计数寄存器(PMCCNTR),它有一个ARM Cortex A7内核.我为它编译了一个内核模块,如下所示: #include #include int in ...
- Linux 多线程 ”一写多读” 模式下的无锁设计
缘起 双buffer "无锁" 设计 指针的切换 ptr 竞争条件的解决 指针访问丢失 延伸 结语 缘起 在linux多线程环境下对同一变量进行读写时,经常会遇到读写的原子性问题, ...
- 用uefi安装linux系统安装win7系统分区,UEFI模式下Win/Linux双系统安装
自从Linux阵营的Ubuntu异军突起之后,双系统的安装一直是简单友好的.先装Windows再装Linux,只要新分区(挂载点 Mount point)的选择没出问题,多系统的Grub启动菜单就会在 ...
- linux vi模式替换,linux基础命令之:vi模式下查找和替换
一.查找 查找命令 /pattern :向下查找pattern匹配字符串 ?pattern:向上查找pattern匹配字符串 使用了查找命令之后,使用如下两个键快速查找: n:按照同一方向继续查找 N ...
- linux网卡主备,linux网卡bounding的主备模式下上层路由端需要什么设置?
不需要做路由设置,给你个列子: # cat /etc/sysconfig/network-scripts/ifcfg-bond0 DEVICE=bond0 BOOTPROTO=none ONBOOT= ...
最新文章
- ios Carthage
- java web mvc思想介绍
- UA MATH571A 一元线性回归III 方差分析与相关性分析
- Boost:bind绑定和或||的测试程序
- 有梦想就有前进的动力
- Jmeter接口测试---加解密
- Vue、J2ee - 001 : Vue项目的创建过程
- Windows Phone开发之 WebClient 讲解
- 千万58招聘人员的选择值得信赖-米苏 58自动循环发帖器
- poker2的配置使用
- 2019重庆大学计算机学院研究生,【计算机】计算机学院举行2019级研究生年级大会...
- SpringBoot整合使用XXL-JOB
- app后端 服务器端 后台 部署图
- 【毕业设计】图像检索算法(以图搜图)
- 使用NGUI模仿制作“切水果”
- 信息爆炸,用写作让它慢下来-2023.04.07
- vr全景三维产品交互展示设计
- UEFI 、GPT 分区安装 Windows10
- Docker: 绿色版docker(带dockerui)安装测试记录_20200120_七侠镇莫尛貝
- android 自定义view,字母排序(仿微信好友列表)
热门文章
- 天猫整站SSM-分页-总结(做个人学习笔记整理用)
- pandas用均值填充nan_python – 如何用pandas中的滚动平均值填充nan值
- (Java)Integer类的其他常用方法
- java openssl dgst_(7) openssl dgst(生成和验证数字签名)
- 的run窗口不显示_「玩转deepin」如何安装VirtualBox增强功能使得deepin全屏显示?...
- 【算法设计与分析】15 分治策略:芯片测试
- IT职业就业-学长有话说
- Python学习之==文件操作
- UVa 11481 (计数) Arrange the Numbers
- 暑假开始了,大家给力啊