参照《Unix网络编程》相关章节内容,实现了一个简单的单线程IO多路复用服务器与客户端。

普通迭代服务器,由于执行recvfrom则会发生阻塞,直到客户端发送数据并正确接收后才能够返回,一个服务器进程只能服务于一个客户端,解决这种问题可采用多线程方式(参见虚拟机隐藏进程检测工具实现)和IO多路复用select和poll,select()机制的优势在于可以同时等待多个描述符就绪。

与IO复用密切相关的另一种IO模型是在多线程中使用阻塞式IO。

简要描述select机制:

fd_set rset

void FD_ZERO(fd_set *fdset)

void FD_SET(int fd, fd_set *fdset)

void FD_CLR(int fd, fd_set *fdset)

void FD_ISSET(int fd, fd_set *fdset)

其中rset类型为fd_set,可理解为一个位图,用于标识哪些描述符正在被监听。FD_ZERO用于初始化rset,FD_SET用于设置新的用于被监听的描述符,FD_CLR用于清空rset,FD_ISSET用户判断具体哪个描述符就绪。

由于tcp服务器中具有监听套接字与已连接套接字两个概念,在服务器端实现中主要过程如下:

  1. 初始化rset,转2;
  2. 添加监听套接字到rset中,转3;
  3. select阻塞等待是否存在描述符就绪,转4;
  4. 当客户端连接后,rset中设置的监听套接字就绪,添加已连接套接字到rset,转5;
  5. 判断哪个描述符就绪(一般为新的已连接套接字),进行套接字读写操作,转1;

在《Unix网络编程》中,上述步骤5结束后直接转2,但在实际测试与资料查阅中,由于内核会修改描述符内容,使得需要重新初始化rset才能有效。

代码测试:

服务端:

 #include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <string.h>
#include <arpa/inet.h> #define IP_ADDR 127.0.0.1
#define IP_PORT 8081 #define LISTEN_NUM 10 #define MAXLINE 1024 int main(void)
{
struct sockaddr_in clitaddr, servaddr;
unsigned int clilen;
int listenfd, connfd, sockfd, ret; int maxindex, i, n; int maxfd;
fd_set allset; int client[FD_SETSIZE];
int nready; char buf[MAXLINE]; int reuse = ; listenfd = socket(AF_INET,SOCK_STREAM,); if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -) {
return -;
} bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(IP_PORT); ret = bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
if(ret == -) {
printf("bind socket error\n");
exit();
} ret = listen(listenfd,LISTEN_NUM);
if(ret == -) {
printf("listen socket error\n");
exit();
} maxfd = listenfd;
maxindex = -;
for(i = ; i < FD_SETSIZE; i++) {
client[i] = -;
}
//FD_ZERO(&allset);
//FD_SET(listenfd, &allset); for( ; ; ) {
//reset = allset;
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for (i = ; i <= maxindex; i++) {
FD_SET(client[i], &allset);
} nready = select(maxfd + , &allset, NULL, NULL, NULL); if(FD_ISSET(listenfd, &allset)) {
clilen = sizeof(clitaddr);
connfd = accept(listenfd, (struct sockaddr*)&clitaddr, &clilen);
//connfd = accept(listenfd, (struct sockaddr*)&clitaddr, sizeof(clitaddr)); //printf("clitaddr ip : %d,port : %d\n", inet_ntoa(clitaddr.sin_addr), clitaddr.sin_port);
fprintf(stdout, "accept clitaddr %s:%d\n", inet_ntoa(clitaddr.sin_addr), clitaddr.sin_port);
for(i = ; i < FD_SETSIZE; i++) {
if(client[i] < ){
client[i] = connfd;
break;
}
} if(i == FD_SETSIZE) {
printf("too many clients\n");
exit();
} FD_SET(connfd, &allset);
if(connfd > maxfd) {
maxfd = connfd;
}
if(i > maxindex) {
maxindex = i;
}
if(--nready <= )
continue;
} for(i = ; i <= maxindex; i++) {
if((sockfd = client[i]) < )
continue;
if(FD_ISSET(sockfd, &allset)) {
if((n = recv(sockfd, buf, MAXLINE, )) == ) {
close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -;
}
else {
printf("server recv buf is %s\n", buf);
send(sockfd, buf, strlen(buf), );
}
if(--nready <= )
break;
}
}
} return ;
}

客户端:

 #include <netinet/in.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <math.h> #define SERVER_ADDR "127.0.0.1"
#define SERVER_PORT 8081 #define MAXLINE 1024 void str_cli(FILE *fp, int sockfd)
{
int maxfdp, stdineof;
fd_set rset;
char buf[MAXLINE];
int n; stdineof = ;
FD_ZERO(&rset); for( ; ; ) {
if(stdineof == )
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp = (fileno(fp) > sockfd) ? (fileno(fp) + ) : (sockfd + );
//maxfdp = max(fileno(fp), sockfd) + 1;
select(maxfdp, &rset, NULL, NULL, NULL); if(FD_ISSET(sockfd, &rset)) {
if ((n = recv(sockfd, buf, MAXLINE, )) == ) {
if (stdineof = ) {
return ;
}
else {
printf("server prematurely\n");
exit();
}
}
printf("client:sockfd ready\n");
//write(fileno(stdout), buf, n);
write(fileno(stdout), buf, strlen(buf) + );
} if (FD_ISSET(fileno(fp), &rset)) {
if ((n = read(fileno(fp), buf, MAXLINE)) == ) {
stdineof = ;
shutdown(sockfd, SHUT_WR);
FD_CLR(fileno(fp), &rset);
continue;
}
//buf[n] = '\0';
printf("client buf is %s",buf);
send(sockfd, buf, strlen(buf), );
}
}
return ;
} int main(void)
{
int sockfd, ret;
struct sockaddr_in servaddr; sockfd = socket(AF_INET, SOCK_STREAM, );
if(sockfd < ) {
printf("create socket error\n");
exit();
} bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERVER_PORT);
servaddr.sin_addr.s_addr = inet_addr(SERVER_ADDR); ret = connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
if(ret < ) {
printf("connect error\n");
exit();
} str_cli(stdin, sockfd);
exit();
}

执行结果:

服务器启动后处于监听状态,可以启动多个客户端连入服务器请求并响应。实验结果略。

io多路复用-select()的更多相关文章

  1. 第五十五节,IO多路复用select模块加socket模块,伪多线并发

    IO多路复用select模块加socket模块,伪多线并发,并不是真正的多线程并发,实际通过循环等待还是一个一个处理的 IO多路复用,lo就是文件或数据的输入输出,IO多路复用就是可以多用户操作 IO ...

  2. Linux IO多路复用 select

    Linux IO多路复用 select 之前曾经写过简单的服务器,服务器是用多线程阻塞,客户端每一帧是用非阻塞实现的 后来发现select可以用来多路IO复用,就是说可以把服务器这么多线程放在一个线程 ...

  3. 转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

    下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...

  4. Python实战之IO多路复用select的详细简单练习

    IO多路复用 I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. select   它通过一个select()系统调用来 ...

  5. I/O模型系列之五:IO多路复用 select、poll、epoll

    IO多路复用之select.poll.epoll IO多路复用:通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. 应用:适用于针 ...

  6. IO多路复用select/poll/epoll详解以及在Python中的应用

    IO multiplexing(IO多路复用) IO多路复用,有些地方称之为event driven IO(事件驱动IO). 它的好处在于单个进程可以处理多个网络IO请求.select/epoll这两 ...

  7. IO多路复用 select、poll、epoll

    什么是IO多路复用 在同一个线程里面, 通过拨开关的方式,来同时传输多个(socket)I/O流. 在英文中叫I/O multiplexing.这里面的 multiplexing 指的其实是在单个线程 ...

  8. Python异步非阻塞IO多路复用Select/Poll/Epoll使用,线程,进程,协程

    1.使用select模拟socketserver伪并发处理客户端请求,代码如下: import socket import select sk = socket.socket() sk.bind((' ...

  9. 网络编程socket 结合IO多路复用select; epool机制分别实现单线程并发TCP服务器

    select版-TCP服务器 1. select 原理 在多路复用的模型中,比较常用的有select模型和epoll模型.这两个都是系统接口,由操作系统提供.当然,Python的select模块进行了 ...

随机推荐

  1. Struts2自定义结果视图(servlet验证码)

    1.编写一个类实现com.opensymphony.xwork2.Result,或者继承org.apache.struts2.dispatcher.StrutsResultSupport 2.自定义的 ...

  2. 深入理解:java类加载器

    概念理解:Java类加载器总结 1.深入理解Java类加载器(1):Java类加载原理解析 2.深入理解Java类加载器(2):线程上下文类加载器

  3. 《转》HTTP 协议入门

    HTTP 协议是互联网的基础协议,也是网页开发的必备知识,最新版本 HTTP/2 更是让它成为技术热点. 本文介绍 HTTP 协议的历史演变和设计思路. 一.HTTP/0.9 HTTP 是基于 TCP ...

  4. [NOIP2012]疫情控制 贪心 二分

    题面:[NOIP2012]疫情控制 题解: 大体思路很好想,但是有个细节很难想QAQ 首先要求最大时间最小,这种一般都是二分,于是我们二分一个时间,得到一个log. 然后发现一个军队,越往上走肯定可以 ...

  5. [AHOI2009]中国象棋 DP,递推,组合数

    DP,递推,组合数 其实相当于就是一个递推推式子,然后要用到一点组合数的知识 一道很妙的题,因为不能互相攻击,所以任意行列不能有超过两个炮 首先令f[i][j][k]代表前i行,有j列为一个炮,有k列 ...

  6. BZOJ2115:[WC2011]Xor——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=2115 https://www.luogu.org/problemnew/show/P4151 这道 ...

  7. CF449C:Jzzhu and Apples——题解

    https://vjudge.net/problem/CodeForces-449C 题目大意:1-n编号的苹果两两一对,他们的最大公约数不为1,求这些对的最大匹配. ———————————————— ...

  8. fhq_treap 学习笔记

    前言:昨天写NOIp2017队列,写+调辗转了3h+,不知道怎么的,就点进了一个神仙的链接,便在今日学习了神仙的fhq_treap. 简介:fhq_treap功能强大,支持splay支持的所有操作,代 ...

  9. HDOJ(HDU).3466 Dividing coins ( DP 01背包 无后效性的理解)

    HDOJ(HDU).3466 Dividing coins ( DP 01背包 无后效性的理解) 题意分析 要先排序,在做01背包,否则不满足无后效性,为什么呢? 等我理解了再补上. 代码总览 #in ...

  10. iOS中产生随机数的方法

    利用arc4random_uniform()产生随机数 Objective-C 中有个arc4random()函数用来生成随机数且不需要种子,但是这个函数生成的随机数范围比较大,需要用取模的算法对随机 ...