不为客户连接创建子进程的并发回射服务器( select实现 )
前言
在此前,我已经介绍了一种并发回射服务器实现( 点此进入 )。它通过调用fork函数为每个客户请求创建一个子进程。同时,我还为此服务器添加了自动消除僵尸子进程的机制。现在请想想,在客户量非常大的情况下,这种为每个客户请求都创建子进程的做法是不是太费资源了?我们可不可以在不为每个客户请求都创建子进程的前提下实现并发回射服务器?答案自然是肯定的,这也是此文要讲述的方法。
实现思路
服务器建立好监听套接字之后,进入以下循环中:
1. 调用select函数,先使之阻塞于监听套接字描述符( 之后阻塞于监听套接字描述符和已连接套接字描述符 )。
2. 当监听套接字描述符被激活,就调用connect函数建议一个客户连接,并将返回的已连接套接字描述符也登记到select函数的监听描述符集中。( 这里我们需要再设立一个cli数组记录下所有的已连接套接字描述符在select函数的监听描述符集中的位置 )。
3. 遍历这个client数组,一旦发现有已连接的监听套接字被激活,则做出相应处理。
代码实现
#include "unp.h" int
main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); // maxfd 表示select函数的监听描述符集中的最大值( 作为第一个参数传递给select )。
maxfd = listenfd;
// maxi 表示client数组的边界
maxi = -;
for (i = ; i < FD_SETSIZE; i++)
client[i] = -;
FD_ZERO(&allset);
FD_SET(listenfd, &allset); for ( ; ; ) {
rset = allset;
nready = Select(maxfd+, &rset, NULL, NULL, NULL); if (FD_ISSET(listenfd, &rset)) {
clilen = sizeof(cliaddr);
// 和客户建立连接
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
#ifdef NOTDEF
printf("new client: %s, port %d\n",
Inet_ntop(AF_INET, &cliaddr.sin_addr, , NULL),
ntohs(cliaddr.sin_port));
#endif
// 将已连接套接字描述符登记进client数组
for (i = ; i < FD_SETSIZE; i++)
if (client[i] < ) {
client[i] = connfd;
break;
}
if (i == FD_SETSIZE)
err_quit("too many clients"); // 将已连接套接字描述符登记进select函数的监听套接字描述符集中
FD_SET(connfd, &allset); /*
* 下面两个if语句在获取到已连接描述符后更新maxfd和maxi。
*/
if (connfd > maxfd)
maxfd = connfd;
if (i > maxi)
maxi = i; // 如果所有被监听的信号已经处理完毕了,则立即启动下一次循环。
if (--nready <= )
continue;
} // 检查select监听描述符集中所有的已连接套接字描述符是否有被激活的,有则做出相应处理。
for (i = ; i <= maxi; i++) {
if ( (sockfd = client[i]) < )
continue;
if (FD_ISSET(sockfd, &rset)) {
if ( (n = Read(sockfd, buf, MAXLINE)) == ) {
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -;
} else
Writen(sockfd, buf, n); // 如果所有被监听的信号已经处理完毕了,则立即启动下一次循环。
if (--nready <= )
break;
}
}
}
}
说明
1. 设置client数组是必须的,它用来标记select的监听描述符集中哪些位处于被监听状态。
2. 使用这种技术的程序容易收到拒绝服务攻击。
不为客户连接创建子进程的并发回射服务器( select实现 )的更多相关文章
- 第十九篇:不为客户连接创建子进程的并发回射服务器(select实现)
前言 在此前,我已经介绍了一种并发回射服务器实现.它通过调用fork函数为每个客户请求创建一个子进程.同时,我还为此服务器添加了自动消除僵尸子进程的机制.现在请想想,在客户量非常大的情况下,这种为每个 ...
- 第二十篇:不为客户连接创建子进程的并发回射服务器(poll实现)
前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...
- 不为客户连接创建子进程的并发回射服务器( poll实现 )
前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...
- socket编程之并发回射服务器2
承接上文:socket编程之并发回射服务器 为了让服务器进程的终止一经发生,客户端就能检测到,客户端需要能够同时处理两个描述符:套接字和用户输入. 可以使用select达到这一目的: void str ...
- socket编程之并发回射服务器
使用到的函数: // 子进程返回0,父进程返回子进程ID,出错返回-1 pid_t fork(void); pid_t wait(int *wstatus); // 最常用的option是WNOHAN ...
- socket编程之并发回射服务器3
在socket编程之并发回射服务器一文中,服务器采用多进程的方式实现并发,本文采用多线程的方式实现并发. 多线程相关API: // Compile and link with -pthread int ...
- 并发回射服务器的最基本实现思路( fork )
前言 一个服务器,通常会在一段时间内接收到多个请求.如果非要等到处理完一个请求再去处理下一个,势必会造成大部分用户的不满( 尤其当有某个请求需要占用大量时间时 ).如何解决这个问题?让处理这些用户请求 ...
- 服务器编程入门(11)TCP并发回射服务器实现 - 单线程select实现
问题聚焦: 当客户端阻塞于从标准输入接收数据时,将读取不到别的途径发过来的必要信息,如TCP发过来的FIN标志. 因此,进程需要内核一旦发现进程指定的一个或多个IO条件就绪(即输入已准备好被读取,或者 ...
- 服务器编程入门(10)TCP回射服务器实现 - 并发
问题聚焦: 在前面我们大概浏览了一下服务器编程需要掌握的一些知识和技术,以及架构思想. 实践,才是检验真理的唯一标准..从这节起我们将在这些技术的基础上,一步步实现以及完善一个服 ...
随机推荐
- 你能说出SQL聚集索引和非聚集索引的区别吗?
最近突然想起前一阵和一朋友的聊天,当时他问我的问题是一个非常普通的问题:说说SQL聚集索引和非聚集索引的区别. AD:WOT2015 互联网运维与开发者大会 热销抢票 其实对于非专业的数据库操作人员来 ...
- LOJ#2302. 「NOI2017」整数
$n \leq 1000000$个操作:一,给$x$加上$a*2^b$:二,问$x$的某个二进制位$k$.$b,k \leq 30n$,$|a| \leq 1e9$. 30暴露了一切..可以把30个二 ...
- 安装apache2.4和php7.1的方法记录
下载扩展 这是PHP官方扩展下载的地址 http://pecl.php.net 点击下载dll文件,放到php的ext目录. 看这个说明下载你PHP版本支持的版本.
- LeetCode OJ-- Simplify Path **
https://oj.leetcode.com/problems/simplify-path/ 对linux路径的规范化,属于字符串处理的题目.细节很多. #include <iostream& ...
- WinRT 开发:在 MVVM 模式中,关于绑定的几处技巧
以下会提到三个绑定的技巧,分别是 在 ListView 中为 ListViewItem 的 MenuFlyout 绑定 Command: 在 ListView 的 事件中绑定所选择项目,即其 Sele ...
- windows命令行更改IP地址
修改IP地址时,提示出现一个意外情况 netsh interface ip set address "以太网" static 192.168.3.151 255.255.255.0 ...
- 利用github搭建个人网站
1.注册一个github https://github.com/ 2.新建一个仓库 仓库名 用 Owner.github.io 的格式,然后点击创建 3.源码上传至github 安装github桌 ...
- hdu6217(数学)
题意: 你需要输出在16进制下,π的第n位的数字 分析: 既然要求第n位的数字,我们不妨把原来的数字乘上$16^{n-1}$,我们要求的就是这个和式的小数部分的最高位 我们可以用double暴力求出小 ...
- xamarin android 获取根证书代码
Java.Security.KeyStore keyStore = Java.Security.KeyStore.GetInstance("AndroidCAStore"); ke ...
- 为什么HierachyViewer无法连接真机调试
关于什么是Hierarchy Viewer,请查看官方文档:http://developer.android.com/tools/debugging/debugging-ui.html.个人理解:Hi ...