服务器编程入门(11)TCP并发回射服务器实现 - 单线程select实现
当客户端阻塞于从标准输入接收数据时,将读取不到别的途径发过来的必要信息,如TCP发过来的FIN标志。
- 单进程,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, ); const char* ip = argv[];
int port = atoi( argv[] ); /* 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, ); /* 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 = -;
for ( i=; i < FD_SETSIZE; i++ )
client[i] = -; FD_ZERO(&allset);
FD_SET(listenfd, &allset); printf("Waiting for connecting...\n"); for(;;) {
rset = allset;
if ( (nready=select(maxfd+, &rset, NULL, NULL, NULL)) < ) {
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 = ; i < FD_SETSIZE; i++) {
if ( client[i] < ) {
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 <= ) {
continue;
}
} /* handle accept fds(client[]) and handle recv or respond msg */
for ( i = ; i <= maxi; i++) {
if ( (sockfd = client[i]) < )
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] = -;
} if (--nready <= )
break;
}
}
}
} bool handle_recv(int connfd) { char recvbuf[BUFSIZE]; memset( recvbuf, '\0', BUFSIZE );
if ( recv(connfd, recvbuf,BUFSIZE,) != ) {
if (!strcmp(recvbuf, "exit"))
return true;
fprintf(stderr,"recv msg: \"%s\" from connfd:%d\n", recvbuf, connfd);
send(connfd, recvbuf, strlen(recvbuf), );
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, ); int port = atoi(argv[]);
char* ip = argv[]; int sockfd;
struct sockaddr_in servaddr; mySocket(sockfd); initSockAddr(servaddr,ip, port); myConnect(sockfd,
(struct sockaddr*)&servaddr,
sizeof(servaddr)); handle_msg(sockfd);
exit(); } void handle_msg(int sockfd) { char sendbuf[BUFSIZE];
char recvbuf[BUFSIZE]; int maxfdpl, ret;
fd_set rset;
int normalTermi = ; FD_ZERO(&rset); while() {
memset( sendbuf, '\0', BUFSIZE );
memset( recvbuf, '\0', BUFSIZE ); if (normalTermi == )
FD_SET( , &rset ); FD_SET( sockfd, &rset );
maxfdpl = sockfd + ; if(DEBUG)
printf("Debug: waiting in select\n");
if ( select( maxfdpl, &rset, NULL, NULL, NULL) < ) {
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, ) == ) { if(DEBUG)
printf("Debug: ready to quit, normalTermi: %d\n" ,
normalTermi); if (normalTermi == ) {
printf("handle_msg: normal terminated.\n");
return;
}
else {
printf("handle_msg: server terminated.\n");
exit();
}
}
fprintf(stderr,
"recv back: %s\n",
recvbuf);
}
else if ( FD_ISSET( , &rset ) ) {
gets(sendbuf);
if (strlen(sendbuf) > ) {
send(sockfd, sendbuf, strlen(sendbuf), );
if ( !strcmp(sendbuf, "exit") ) {
normalTermi = ;
shutdown(sockfd, SHUT_WR);
FD_CLR(, &rset);
continue;
}
}
}
}
close( sockfd );
return;
}

服务器编程入门(11)TCP并发回射服务器实现 - 单线程select实现的更多相关文章
- socket编程之并发回射服务器3
在socket编程之并发回射服务器一文中,服务器采用多进程的方式实现并发,本文采用多线程的方式实现并发. 多线程相关API: // Compile and link with -pthread int ...
- socket编程之并发回射服务器2
承接上文:socket编程之并发回射服务器 为了让服务器进程的终止一经发生,客户端就能检测到,客户端需要能够同时处理两个描述符:套接字和用户输入. 可以使用select达到这一目的: void str ...
- socket编程之并发回射服务器
使用到的函数: // 子进程返回0,父进程返回子进程ID,出错返回-1 pid_t fork(void); pid_t wait(int *wstatus); // 最常用的option是WNOHAN ...
- 第二十篇:不为客户连接创建子进程的并发回射服务器(poll实现)
前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...
- 不为客户连接创建子进程的并发回射服务器( poll实现 )
前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...
- 第十九篇:不为客户连接创建子进程的并发回射服务器(select实现)
前言 在此前,我已经介绍了一种并发回射服务器实现.它通过调用fork函数为每个客户请求创建一个子进程.同时,我还为此服务器添加了自动消除僵尸子进程的机制.现在请想想,在客户量非常大的情况下,这种为每个 ...
- 不为客户连接创建子进程的并发回射服务器( select实现 )
前言 在此前,我已经介绍了一种并发回射服务器实现( 点此进入 ).它通过调用fork函数为每个客户请求创建一个子进程.同时,我还为此服务器添加了自动消除僵尸子进程的机制.现在请想想,在客户量非常大的情 ...
- 并发回射服务器的最基本实现思路( fork )
前言 一个服务器,通常会在一段时间内接收到多个请求.如果非要等到处理完一个请求再去处理下一个,势必会造成大部分用户的不满( 尤其当有某个请求需要占用大量时间时 ).如何解决这个问题?让处理这些用户请求 ...
- 基本 TCP 的回射服务器
实验一 代码:链接[01项目] 1. 先启动服务器,如图: 2. 然后启动客户端,如图: 3. 输出结果: [注意]:在服务器终止时,给父进程发送了一个SIGCHILD信号,这一点本例发生了,但是我们 ...
随机推荐
- PHP学习之-1.5 字符串
字符串 一个字符串是用双引号扩起来的一个词或者一个句子,比如 "Hello Word" ,你可以使用PHP语言输入这个字符串,像这样 <?php echo "Hel ...
- 零基础数据分析与挖掘R语言实战课程(R语言)
随着大数据在各行业的落地生根和蓬勃发展,能从数据中挖金子的数据分析人员越来越宝贝,于是很多的程序员都想转行到数据分析, 挖掘技术哪家强?当然是R语言了,R语言的火热程度,从TIOBE上编程语言排名情况 ...
- Swift - 使用TableView的静态单元格进行页面布局
通过使用静态单元格的列表,我们可以很方便的进行页面布局.下面通过一个“添加任务页面”来进行演示. 效果图如下: 实现步骤: 1,在storyboard中拖入一个TableViewController, ...
- 简易的sniffer程序
真的非常简易,这个程序不过抓一些发送到本机的数据包,然后显示出来它们的一些信息罢了. 程序很easy! #include <WinSock2.h> #include ...
- Effective C++_笔记_条款04_确定对象被使用之前已先被初始化
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 读取未初始化的值会导致不确定的行为.在某些平台上,仅仅只是读取为 ...
- Irvine的专业汇编网站
http://asmirvine.com/ http://download.csdn.net/download/stupid_boy2007/3890853 http://download.csdn. ...
- poj2418 map 快排水过
/*计算每个单词的百分比按字典序输出*/ #include <cstdio>#include<cstdlib>#include <cstring>#include ...
- ajax后台处理返回json值
public ActionForward xsearch(ActionMapping mapping, ActionForm form, HttpServletRequest request, Htt ...
- 积累的VC编程小技巧之按钮
1.资源种创建的控件,对其属性的动态控制: 在对话框类的头文件里创建所要改变属性的控件的对象,如要改变一个Button(其ID为IDC_MyButton)的属性,则需创建Cbutton的对象m_but ...
- 汇编与高级语言(插图结合Delphi代码,来自linzhengqun)
汇编与高级语言 1. 汇编基础知识 1.1. 寄存器 寄存器 用途 EAX,EBX,EDX,ECX 通用寄存器,由程序员自己指定用途,也有一些不成文的用法: EAX:常用于运算. ...