服务器编程入门(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信号,这一点本例发生了,但是我们 ...
随机推荐
- perl post发送json数据
sub wx_init { #$login_url ="https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=- ...
- Cloudera Hadoop 4 实战课程(Hadoop 2.0、集群界面化管理、电商在线查询+日志离线分析)
课程大纲及内容简介: 每节课约35分钟,共不下40讲 第一章(11讲) ·分布式和传统单机模式 ·Hadoop背景和工作原理 ·Mapreduce工作原理剖析 ·第二代MR--YARN原理剖析 ·Cl ...
- EasyUI - 操作 Tree 控件
效果: HTML代码: 使用了模板页 <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHo ...
- JQuery 插件之Ajax Autocomplete(ajax自动完成)
平时用百度,谷歌搜索的时候 会有一个下 拉列表进行提示 这是一个非常好的功能 本文要介绍的这个JQuery 插件 名叫Ajax Autocomplete 顾名思义 ajax 也就是用ajax的方式获取 ...
- android使用xfire webservice框架远程对sqlserver操作(包括增删改查)的实例!!已在真机上试验通过
前两天,公司有一个利用android远程操作sqlserver的项目,对此我是毫无头绪的,但也挺感兴趣的,于是开始上网搜索方法,网上有挺多方法了,发现使用webservice的挺多的,不过我对这些技术 ...
- JavaScript的原型继承
JavaScript是一门面向对象的语言.在JavaScript中有一句很经典的话,万物皆对象.既然是面向对象的,那就有面向对象的三大特征:封装.继承.多态.这里讲的是JavaScript的继承,其他 ...
- J2SE学习小结
开始接触Java的学习,Java 2 Standard Edition为Java2平台的标准版,其包括了构成Java语言核心的类,此番学习算是学习了Java体系中的基础部分. 学习框架大致整理如下: ...
- tsunami:一种基于UDP协议的高速传输
一. 需求 近期在做数据库迁移.常常须要打包实例传输.传统scp感觉非常慢. 二. 软件信息 1. 软件主页:http://tsunami-udp.sf.net/ 2. 软件安装:直接源代码make ...
- inline与lnk2001、lnk2019,鸡肋?
inline函数居然出现了lnk2001.lnk2019,先贴代码. a.h #pragma once class A { public: inline void foo(); void us ...
- Win7和VS2013上使用Intel的TBB
源地址:http://www.th7.cn/system/win/201505/103966.shtml http://wenku.baidu.com/link?url=zH7vwmWltWF5R-9 ...