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重新实现非阻塞的回射服务器相关推荐

  1. Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合

    上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型. 阻塞IO 过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器为例,我 ...

  2. linux——回射服务器

    回射服务器即客户端发送一段数据给服务器,服务器再将这段数据原封不动的发送给客户端,原理很简单,原理图如下: 以TCP协议为例,客户端.服务器代码如下: ** 服务器: ** #include < ...

  3. Linux内核剖析-----IO复用函数epoll内核源码剖析

    本文参考董浩博客 http://donghao.org/uii/   epoll内核实现 (1)内核为epoll做准备工作 这个模块在内核初始化时(操作系统启动)注册了一个新的文件系统,叫" ...

  4. UNIX TCP回射服务器/客户端之使用epoll模型的服务器

    程序简介:这是一个运用epoll系列函数进行IO复用的服务器模型.它是目前UNIX与LINUX平台上效率最高,最受欢迎的IO复用传输模型. 其他的不说了,直接粘贴代码吧! 服务器端: #include ...

  5. Linux——回射服务器多并发(多线程)

    多线程与多进程的做法区别不大,思路一样,都是执行两个死循环,一个循环等待客户端连接,一个循环与客户端通信.多进程的方式请点此处 服务器 #include <sys/socket.h> #i ...

  6. linux——回射服务器多并发(多进程)

    多并发原理如图,多个客户端连接一个服务器,无论哪个客户端发送数据给服务器,服务器都能把数据准确的返回给这个客户端. 在socket编程中,socket这种文件描述符被默认设置为阻塞,故而read函数和 ...

  7. Linux C++ 回射服务器

    http://blog.csdn.net/qq_25425023/article/details/53914820 回射服务器就是服务端将客户端的数据发送回去. 我实现的回射服务器返回增加了时间. 服 ...

  8. 2022-4-16 splice实现回射服务器《Linux高性能服务器笔记》

    服务端 #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include&l ...

  9. 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO

    一.基本概念 我们通俗一点讲: Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写.如果这次没有把数据一次性全部读写完( ...

最新文章

  1. Serenity安装和创建DEMO--学习第一天
  2. Crawler:基于Crawler和P2P技术实现机器人智能在线下载
  3. idea报错解决:Error: A JNI error has occurred, please check your installation and try again
  4. 计算机位数与内存相关,弄懂电脑的各种位数、内存、存储
  5. Java并发编程—无锁互斥机制及CAS原理
  6. 如此理解面向对象编程
  7. java 开关按钮_Java Swing JToggleButton开关按钮的实现
  8. django ajax获取数据类型,Django:使用Ajax获取模板中的数据库对象值
  9. cad怎么卸载干净_CAD无法卸载,又无法安装怎么办?
  10. Quartus II 13.1 安装步骤详解
  11. 宏观经济指标分析与数据可视化——PMI
  12. 加州大学戴维斯计算机博士生,关于加州大学戴维斯分校博士研究生CSC奖学金信息分享会的通知...
  13. 微信小程序多次跳转后不能点_微信突然更新,但我劝你这次别升级
  14. [NAS] Synology (群晖) DSM7.0 使用自定义供应商DDNS
  15. FFmpeg从入门到入魔(2):保存流到本地MP4
  16. IBM公司利用人工智能预测化学反应结果
  17. 排查【Jedis客户端触发 Too many open files 问题】
  18. C语言频率计程序,基于单片机的频率计的C语言源代码
  19. 使用TimeUnit类中的sleep代替Thread.sleep
  20. django 下载安装xadmin(django3.x + python3.6+xadmin2.x)

热门文章

  1. 科学互驳:大脑细胞活到老,长到老?
  2. 潘建伟团队进行人类首次洲际量子通信,给奥地利发去了什么?
  3. 大厂围城:千辛万苦杀进来,为何他们选择出逃?
  4. 回溯 Rust 2020:正成为最受欢迎的编程语言
  5. 漫画 | 程序员必须要小心的 7 个潜规则
  6. 阿里技术人的成长路径是什么?
  7. 微软全球 AKS 女掌门人,这样击破云原生“怪圈”!
  8. Docker for windows挂载文件到Nginx目录踩坑小记
  9. P1494 小Z的袜子
  10. 学习Unix,可从事什么样的工作(1)《精通Unix下C语言与项目实践》读书笔记(3)...