关于poll模型监听的事件以及返回事件,我们定义宏如下:

#define kReadEvent (POLLIN | POLLPRI)
#define kWriteEvent (POLLOUT | POLLWRBAND)
#define kReadREvent (POLLIN | POLLPRI | POLLRDHUP)
#define kWriteREvent (POLLOUT)

前面我们说明了,为什么非阻塞IO必须具备缓冲区。事实上,对于server而言,每条TCP连接应该具有两个缓冲区,一个用于输入,一个用于输出。

sockfd –>  InputBuffer –> 用户空间 –> 处理数据 –> 得到结果 –> OutputBuffer –> sockfd

但是本例仅仅是简单的回射服务,收到数据立刻发出,所以我们只使用一个缓冲区。

对于每个TCP连接,我们对应这样的一个数据结构:

typedef struct{
buffer_t buffer_;
} tcp_connection_t; //表示一条TCP连接

那么我们需要建立一个从fd到对应的tcp_connection_t的对应关系。我使用以下的数组:

tcp_connection_t *connsets[EVENTS_SIZE]; //提供从fd到TCP连接的映射

这个数组初始化为NULL,然后我使用fd作为下标,也就是说,当我需要处理某fd的时候,以该fd为下标就可以找到它相应的tcp_connection_t指针。这本质上是一种哈希表的思想

使用tcp_connection_t的逻辑是:

每当accept一个新的连接,就动态创建一个新的tcp_connection_t,并且将其指针保存至以peerfd为下标的位置中

每当连接关闭,需要释放内存,同时重置指针为NULL。

我将所有的代码全部写入main函数中,因为前面封装了buffer,代码的可读性已经提高,再加上我们的业务逻辑简单,进一步封装,反而降低可读性。

这里跟client有几处相同点:

仅当缓冲区有空闲空间时才监听read事件

仅当缓冲区有数据时,才监听write事件

所以我们每次进行poll调用前都需要将fd的监听事件重新装填。

server的代码整体框架如下:

//初始化poll

while(1)
{
//重新装填fd数组 //poll系统调用 //依次处理每个fd的读写事件
}

所以完整的代码如下:

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <sys/socket.h>
#include "sysutil.h"
#include "buffer.h"
#include <assert.h> #define EVENTS_SIZE 1024 typedef struct{
buffer_t buffer_;
} tcp_connection_t; //表示一条TCP连接 tcp_connection_t *connsets[EVENTS_SIZE]; //提供从fd到TCP连接的映射 int main(int argc, char const *argv[])
{
//获取监听fd
int listenfd = tcp_server("localhost", 9981);
//将监听fd设置为非阻塞
activate_nonblock(listenfd); //初始化connsets
int i;
for(i = 0; i < EVENTS_SIZE; ++i)
{
connsets[i] = NULL;
} //初始化poll
struct pollfd events[EVENTS_SIZE];
for(i = 0; i < EVENTS_SIZE; ++i)
events[i].fd = -1;
events[0].fd = listenfd;
events[0].events = kReadEvent;
int maxi = 0; while(1)
{
//重新装填events数组
int i;
for(i = 1; i < EVENTS_SIZE; ++i)
{
int fd = events[i].fd;
events[i].events = 0; //重置events
if(fd == -1)
continue;
assert(connsets[fd] != NULL); //当Buffer中有数据可读时,才监听write事件
if(buffer_is_readable(&connsets[fd]->buffer_))
{
events[i].events |= kWriteEvent;
} //当Buffer中有空闲空间时,才监听read事件
if(buffer_is_writeable(&connsets[fd]->buffer_))
{
events[i].events |= kReadEvent;
} } //poll调用
int nready = poll(events, maxi + 1, 5000);
if(nready == -1)
ERR_EXIT("poll");
else if(nready == 0)
{
printf("poll timeout.\n");
continue;
} //处理listenfd
if(events[0].revents & kReadEvent)
{
//接受一个新的客户fd
int peerfd = accept4(listenfd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
if(peerfd == -1)
ERR_EXIT("accept4");
//新建tcp连接
tcp_connection_t *pt = (tcp_connection_t*)malloc(sizeof(tcp_connection_t));
buffer_init(&pt->buffer_);
//将该tcp连接放入connsets
connsets[peerfd] = pt;
//放入events数组
int i;
for(i = 0; i < EVENTS_SIZE; ++i)
{
if(events[i].fd == -1)
{
events[i].fd = peerfd; //这里不必监听fd
if(i > maxi)
maxi = i; //更新maxi
break;
}
}
if(i == EVENTS_SIZE)
{
fprintf(stderr, "too many clients\n");
exit(EXIT_FAILURE);
}
} //处理客户fd
//int i;
for(i = 1; i <= maxi; ++i)
{
int sockfd = events[i].fd;
if(sockfd == -1)
continue;
//取出指针
tcp_connection_t *pt = connsets[sockfd];
assert(pt != NULL);
if(events[i].revents & kReadREvent) //读取数据
{
if(buffer_read(&pt->buffer_, sockfd) == 0)
{
//close
events[i].fd = -1;
close(sockfd);
free(pt);
connsets[sockfd] = NULL;
continue; //继续下一次循环
}
} if(events[i].revents & kWriteREvent) //可以发送数据
{
buffer_write(&pt->buffer_, sockfd);
}
}
} close(listenfd); return 0;
}

需要注意的是,关于accept,我使用的是accpet4,这是Linux新增的系统调用:

 

int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);

与accept的区别就是accept4可以指定非阻塞标志位。

 

下文使用epoll。

Linux非阻塞IO(六)使用poll实现非阻塞的服务器端的更多相关文章

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

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

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

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

  3. Linux下的非阻塞IO(一)

    非阻塞IO是相对于传统的阻塞IO而言的. 我们首先需要搞清楚,什么是阻塞IO.APUE指出,系统调用分为两类,低速系统调用和其他,其中低速系统调用是可能会使进程永远阻塞的一类系统调用.但是与磁盘IO有 ...

  4. python 全栈开发,Day44(IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)

    昨日内容回顾 协程实际上是一个线程,执行了多个任务,遇到IO就切换 切换,可以使用yield,greenlet 遇到IO gevent: 检测到IO,能够使用greenlet实现自动切换,规避了IO阻 ...

  5. {python之IO多路复用} IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) 多路复用IO(IO multiplexing) 异步IO(Asynchronous I/O) IO模型比较分析 selectors模块

    python之IO多路复用 阅读目录 一 IO模型介绍 二 阻塞IO(blocking IO) 三 非阻塞IO(non-blocking IO) 四 多路复用IO(IO multiplexing) 五 ...

  6. (IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)

    参考博客: https://www.cnblogs.com/xiao987334176/p/9056511.html 内容回顾 协程实际上是一个线程,执行了多个任务,遇到IO就切换 切换,可以使用yi ...

  7. 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

    同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出 ...

  8. 转 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

    此文章为转载,如有侵权,请联系本人.转载出处,http://blog.chinaunix.net/uid-28458801-id-4464639.html 同步(synchronous) IO和异步( ...

  9. 阻塞IO、非阻塞IO的区别

    1.类与类之间的关系:依赖,实现,泛化(继承),关联,组合,聚合. 1)依赖(虚线):一个类是 另一个类的函数参数 或者 函数返回值. 2)实现(实线加小圆):对纯虚函数类(抽象类)的实现. 3)继承 ...

  10. 5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO

    POSIX 同步IO.异步IO.阻塞IO.非阻塞IO,这几个词常见于各种各样的与网络相关的文章之中,往往不同上下文中它们的意思是不一样的,以致于我在很长一段时间对此感到困惑,所以想写一篇文章整理一下. ...

随机推荐

  1. 我读过的最好的epoll讲解--转自”知乎“ 【转】

    转自:http://blog.csdn.net/xu3737284/article/details/12715963 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O ...

  2. 华为上机测试题(数字字符串转二进制-java)

    PS:此题满分,可参考 /*  * 题目:数字字符串转二进制 * 描述: 输入一串整数,将每个整数转换为二进制数,如果倒数第三个Bit是“0”,则输出“0”,如果是“1”,则输出“1”. 题目类别: ...

  3. Linux C/C++内存泄漏检测工具:Valgrind

    Valgrind 是一款 Linux下(支持 x86.x86_64和ppc32)程序的内存调试工具,它可以对编译后的二进制程序进行内存使用监测(C语言中的malloc和free,以及C++中的new和 ...

  4. 关于python浮点数的精度问题。

    若想严格按照四舍五入进行,可使用Decimal,代码如下: from decimal import Decimal, ROUND_HALF_UP def round(x, n): return Dec ...

  5. 计蒜客 28315.Excellent Engineers-线段树(单点更新、区间最值) (Benelux Algorithm Programming Contest 2014 Final ACM-ICPC Asia Training League 暑假第一阶段第二场 E)

    先写这几道题,比赛的时候有事就只签了个到. 题目传送门 E. Excellent Engineers 传送门 这个题的意思就是如果一个人的r1,r2,r3中的某一个比已存在的人中的小,就把这个人添加到 ...

  6. HDU 4920.Matrix multiplication-矩阵乘法

    Matrix multiplication Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/ ...

  7. poj2763(树链剖分 - 边权)

    poj2763 题意 给定一个树形图,某人原来在 s 点,每条边(路)有通过的时间花费,有两种操作:1. 查询某人到 u 点花费的时间 2. 更新某条路的时间花费. 分析 权值在边上,可以把它们 &q ...

  8. Jmeter进行webSocket接口测试

    一.运行Jmeter (1) 去官方网站下载jmeter(版本为3.3)并解压.点击bin/jmeter.bat启动jmeter (2)  新建线程组. (3) 在线程组中新建WebSocket Sa ...

  9. luogu P1347 排序

    题目描述 一个不同的值的升序排序数列指的是一个从左到右元素依次增大的序列,例如,一个有序的数列A,B,C,D 表示A<B,B<C,C<D.在这道题中,我们将给你一系列形如A<B ...

  10. 【bzoj2393】【Cirno的完美算数教室】容斥原理的剪枝应用

    (上不了p站我要死了,侵权度娘背锅) 在用容斥定理时,常常会用到dfs的形式,如果枚举完所有的情况可能会超时,其剪枝的优化很是重要. Description ~Cirno发现了一种baka数,这种数呢 ...