Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器
2019独角兽企业重金招聘Python工程师标准>>>
本文无太多内容,主要是几个前面提到过的注意点:
一是epoll的fd需要重新装填。我们将tcp_connection_t的指针保存在数组中,所以我们以这个数组为依据,重新装填fd的监听事件。
//重新装填epoll内fd的监听事件int i;for(i = 0; i < EVENTS_SIZE; ++i){if(connsets[i] != NULL){int fd = i; //fdtcp_connection_t *pt = connsets[i]; //tcp connuint32_t event = 0;if(buffer_is_readable(&pt->buffer_))event |= kWriteEvent;if(buffer_is_writeable(&pt->buffer_))event |= kReadEvent;//重置监听事件epoll_mod_fd(epollfd, fd, event);}}
二是,建立连接时,需要做的工作是:
1.新建tcp_connection_t结构,初始化
2.将fd加入epoll,不监听任何事件
3.将tcp_connection_t的指针加入数组。
代码如下:
//建立连接
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;
epoll_add_fd(epollfd, peerfd, 0);
连接关闭时需要:
//close
epoll_del_fd(epollfd, fd);
close(fd);
free(pt);
connsets[fd] = NULL;
还有一点:前面我们记录fd和connsets的关系,采用的是数组下标的方式,其实我们还可以将指针存入epoll的data中,其中:
typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64;
} epoll_data_t;struct epoll_event {uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */
};
我们对于data这个联合体,不再使用fd,而是使用ptr,指向一个tcp_connection_t的指针。不过我们需要将fd存储在tcp_connection_t数据结构中。
这里为了简便起见,仍采用以前的方法,读者可以自行尝试。
完整的代码如下:
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <sys/socket.h>
#include "sysutil.h"
#include "buffer.h"
#include <assert.h>
#include <sys/epoll.h>#define EVENTS_SIZE 1024typedef struct{buffer_t buffer_;
} tcp_connection_t; //表示一条TCP连接
tcp_connection_t *connsets[EVENTS_SIZE]; //提供从fd到TCP连接的映射int main(int argc, char const *argv[])
{//获取监听fdint listenfd = tcp_server("localhost", 9981);//将监听fd设置为非阻塞
activate_nonblock(listenfd);//初始化connsetsint ix;for(ix = 0; ix < EVENTS_SIZE; ++ix){connsets[ix] = NULL;}//初始化epollint epollfd = epoll_create1(0);epoll_add_fd(epollfd, listenfd, kReadEvent);struct epoll_event events[1024];while(1){//重新装填epoll内fd的监听事件int i;for(i = 0; i < EVENTS_SIZE; ++i){if(connsets[i] != NULL){int fd = i; //fdtcp_connection_t *pt = connsets[i]; //tcp connuint32_t event = 0;if(buffer_is_readable(&pt->buffer_))event |= kWriteEvent;if(buffer_is_writeable(&pt->buffer_))event |= kReadEvent;//重置监听事件epoll_mod_fd(epollfd, fd, event);}}//epoll监听fdint nready = epoll_wait(epollfd, events, 1024, 5000);if(nready == -1)ERR_EXIT("epoll wait");else if(nready == 0){printf("epoll timeout.\n");continue;}//处理fdfor(i = 0; i < nready; ++i){int fd = events[i].data.fd;uint32_t revents = events[i].events;if(fd == listenfd) //处理listen fd
{if(revents & kReadREvent){//建立连接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连接放入connsetsconnsets[peerfd] = pt;epoll_add_fd(epollfd, peerfd, 0);}}else //处理普通客户的fd
{//取出指针tcp_connection_t *pt = connsets[fd];assert(pt != NULL);if(revents & kReadREvent){if(buffer_read(&pt->buffer_, fd) == 0){//close
epoll_del_fd(epollfd, fd);close(fd);free(pt);connsets[fd] = NULL;continue; //继续下一次循环
} }if(revents & kWriteREvent){buffer_write(&pt->buffer_, fd);}}}}close(listenfd);return 0;
}
下文使用epoll的ET模式。
转载于:https://my.oschina.net/inevermore/blog/388669
Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器相关推荐
- Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合
上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型. 阻塞IO 过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器为例,我 ...
- linux——回射服务器
回射服务器即客户端发送一段数据给服务器,服务器再将这段数据原封不动的发送给客户端,原理很简单,原理图如下: 以TCP协议为例,客户端.服务器代码如下: ** 服务器: ** #include < ...
- Linux内核剖析-----IO复用函数epoll内核源码剖析
本文参考董浩博客 http://donghao.org/uii/ epoll内核实现 (1)内核为epoll做准备工作 这个模块在内核初始化时(操作系统启动)注册了一个新的文件系统,叫" ...
- UNIX TCP回射服务器/客户端之使用epoll模型的服务器
程序简介:这是一个运用epoll系列函数进行IO复用的服务器模型.它是目前UNIX与LINUX平台上效率最高,最受欢迎的IO复用传输模型. 其他的不说了,直接粘贴代码吧! 服务器端: #include ...
- Linux——回射服务器多并发(多线程)
多线程与多进程的做法区别不大,思路一样,都是执行两个死循环,一个循环等待客户端连接,一个循环与客户端通信.多进程的方式请点此处 服务器 #include <sys/socket.h> #i ...
- linux——回射服务器多并发(多进程)
多并发原理如图,多个客户端连接一个服务器,无论哪个客户端发送数据给服务器,服务器都能把数据准确的返回给这个客户端. 在socket编程中,socket这种文件描述符被默认设置为阻塞,故而read函数和 ...
- Linux C++ 回射服务器
http://blog.csdn.net/qq_25425023/article/details/53914820 回射服务器就是服务端将客户端的数据发送回去. 我实现的回射服务器返回增加了时间. 服 ...
- 2022-4-16 splice实现回射服务器《Linux高性能服务器笔记》
服务端 #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include&l ...
- 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO
一.基本概念 我们通俗一点讲: Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写.如果这次没有把数据一次性全部读写完( ...
最新文章
- Serenity安装和创建DEMO--学习第一天
- Crawler:基于Crawler和P2P技术实现机器人智能在线下载
- idea报错解决:Error: A JNI error has occurred, please check your installation and try again
- 计算机位数与内存相关,弄懂电脑的各种位数、内存、存储
- Java并发编程—无锁互斥机制及CAS原理
- 如此理解面向对象编程
- java 开关按钮_Java Swing JToggleButton开关按钮的实现
- django ajax获取数据类型,Django:使用Ajax获取模板中的数据库对象值
- cad怎么卸载干净_CAD无法卸载,又无法安装怎么办?
- Quartus II 13.1 安装步骤详解
- 宏观经济指标分析与数据可视化——PMI
- 加州大学戴维斯计算机博士生,关于加州大学戴维斯分校博士研究生CSC奖学金信息分享会的通知...
- 微信小程序多次跳转后不能点_微信突然更新,但我劝你这次别升级
- [NAS] Synology (群晖) DSM7.0 使用自定义供应商DDNS
- FFmpeg从入门到入魔(2):保存流到本地MP4
- IBM公司利用人工智能预测化学反应结果
- 排查【Jedis客户端触发 Too many open files 问题】
- C语言频率计程序,基于单片机的频率计的C语言源代码
- 使用TimeUnit类中的sleep代替Thread.sleep
- django 下载安装xadmin(django3.x + python3.6+xadmin2.x)
热门文章
- 科学互驳:大脑细胞活到老,长到老?
- 潘建伟团队进行人类首次洲际量子通信,给奥地利发去了什么?
- 大厂围城:千辛万苦杀进来,为何他们选择出逃?
- 回溯 Rust 2020:正成为最受欢迎的编程语言
- 漫画 | 程序员必须要小心的 7 个潜规则
- 阿里技术人的成长路径是什么?
- 微软全球 AKS 女掌门人,这样击破云原生“怪圈”!
- Docker for windows挂载文件到Nginx目录踩坑小记
- P1494 小Z的袜子
- 学习Unix,可从事什么样的工作(1)《精通Unix下C语言与项目实践》读书笔记(3)...