服务器编程入门(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信号,这一点本例发生了,但是我们 ...
随机推荐
- 试解析Tomcat运行原理(一)--- socket通讯(转)
关于这篇文章也确实筹划了很久,今天决定开篇写第一篇,说起tomcat首先很容易联想到IIS,因为我最开始使用的就是.net技术,我第一次使用asp写学生成绩管理系统后,很茫然如何让别人都能看到或者说使 ...
- 性能测试之LoardRunner 自动关联
1.什么是自动关联? 2.实例介绍 以下是详细介绍: 自动化关联:它是VuGen提供的自动化扫描关联处理策略,它的原理是对同一个脚本运行和录制时的服务器返回进行比较,来自动查找变化的部分,并且提示是否 ...
- Java面试宝典2014版
一. Java基础部分......................................................................................... ...
- 使用JDBC获取能自动增加的主键
本篇讲述如何使用JDBC获取能自动增加的主键的值.有时候我们在向数据库插入数据时希望能返回主键的值,而不是通过查询的方式.一般来说,在多表相互关联主键约束,也就是说别的表的外键约束是该表的主键,那么在 ...
- 高斯消元法~get√
高斯消元法,是线性代数中的一个算法,可用来求解线性方程组,并可以求出矩阵的秩,以及求出可逆方阵的逆矩阵.高斯消元法的原理是:若用初等行变换将增广矩阵 化为 ,则AX = B与CX = D是同解方程组. ...
- JavaScript移除数组元素
//数组移除长度方法 var array=[]; array[0]="张三"; array[1]="李四"; array[2]="王五"; ...
- ALV预警灯图标代码
需要先引用TYPE-POOLS: slis,icon. ICON_LED_GREEN 绿灯 ICON_LED_RED红灯 ICON_LED_YELLOW黄灯
- Cygwin的安装及在Android jni中的简单使用举例
Cygwin是一个在windows平台上执行的类UNIX模拟环境,是cygnussolutions公司开发的自由软件.Cygwin是很多自由软件的集合,Cygwin的主要目的是通过又一次编译.将POS ...
- 多字符集(ANSI)和UNICODE及字符串处理方式准则
在我们编写程序的时候,使用最多的是字符串的处理,而ANSI和UNICODE的相互转换经常搞的我们头晕眼乱. 应该说UNICODE是一种比较好的编码方式,在我们的程序中应该尽量使用UNICODE编码方式 ...
- 自学PHP 环境搭建
自学PHP之环境搭建 一..首先 安装 phpStudy2013.exe 程序集成包 安装完可能端口被占用 需要手动设置 然后打开http://localhost:8080/phpMyAdmin/ ...