服务器编程入门(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信号,这一点本例发生了,但是我们 ...
随机推荐
- 北风风hadoop课程体系
课程一.基于Linux操作系统平台下的Java语言开发(20课时)课程简介本套课程主要介绍了Linux系统下的Java环境搭建及最基础的Java语法知识.学习Linux操作系统下Java语言开发的好处 ...
- 基于visual Studio2013解决面试题之0702输出数字
题目
- winform基础——实现简易赈灾物资发放登记系统
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...
- vim 操作指令1
1.vim 在命令行中输入vim,进入vim编辑器 2.i 按一下i键,下端显示 –INSERT–插入命令,在vim中可能任意字符都有作用 3.Esc 退出i(插入)命令进行其它命令使用 4.:r f ...
- Swift - 几种使用数组的数据存储模型
在iOS游戏开发中,比如2048游戏.有时会需要存储N×N数组的数据模型(如3×3,4×4等).这里我们演示了三种实现方式,分别是:一维数组.仿二维数组.自定义二维数组(即矩阵结构). 功能是根据传入 ...
- Xamarin相关学习预估
以前没有开发过app也没有了解过,当然只是用过,现在迫于形势无奈活到老学到老. 初步想了下app相关开发所涉及的知识点 一 app相关资源监视 1.1 网络监视器:https://github.co ...
- [读书笔记]黑客与画家[Hackers.and.Painters]
(书生注:这本书写的不错.针对程序员,可以带来不同角度的想法,有助于反思自己的程序员工作.我甚至从中发现了自己爱用铅笔的原因... 尤其是其中关于黑客的定义,包括黑客认为的乐趣和目的,让人更深层次思 ...
- 很具体GC学习笔记
GC学习笔记 这是我公司同事的GC学习笔记,写得蛮具体的,由浅入深,循序渐进,让人一看就懂,特转到这里. 一.GC特性以及各种GC的选择 1.垃圾回收器的特性 2.对垃圾回收器的选择 2.1 连续 V ...
- android binder机制之——(创建binder服务)
Binder机制编程 前面的几篇文章具体介绍了android中binder机制的方方面面,相信你对binder机制已经有了较深刻的理解.俗话说得好"学以致用",以下我们就通过在 ...
- 如何在eclipse dump Java内存占用情况和打印GC LOG
当使用java开发应用程序发生内存泄露的时候,经常会需要dump内存,然后使用内存分析工具,比如Eclipse Memory Analyzer(一般称作MAT)工具. 本文将介绍如何在eclipse ...