LT:edge-triggered,这是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核就通过epoll告诉你,然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作而导致那个文件描述符不再是就绪状态(比如你在发送,接收或是接受请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核就不会发送更多的通知(only once)。不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。

6.EPOLL模型的使用方法

epoll用到的所有函数都是在头文件sys/epoll.h中声明的,下面简要说明所用到的数据结构和函数:

(1) epoll_data、epoll_data_t、epoll_event

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 */

};

结构体epoll_event 被用于注册所感兴趣的事件和回传所发生待处理的事件。epoll_event 结构体的events字段是表示感兴趣的事件和被触发的事件,可能的取值为:

EPOLLIN:      表示对应的文件描述符可以读;

EPOLLOUT:     表示对应的文件描述符可以写;

EPOLLPRI:     表示对应的文件描述符有紧急的数据可读;

EPOLLERR:     表示对应的文件描述符发生错误;

EPOLLHUP:     表示对应的文件描述符被挂断;

EPOLLET:      表示对应的文件描述符有事件发生;

联合体epoll_data用来保存触发事件的某个文件描述符相关的数据。例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对应的socket文件描述符,可以把这文件描述符赋给epoll_data的fd字段,以便后面的读写操作在这个文件描述符上进行。

(2)epoll_create

函数声明:intepoll_create(intsize)

函数说明:该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围。

(3) epoll_ctl函数

函数声明:intepoll_ctl(int epfd,int op, int fd, struct epoll_event *event)

函数说明:该函数用于控制某个文件描述符上的事件,可以注册事件、修改事件、删除事件。

epfd:由 epoll_create 生成的epoll专用的文件描述符;

op:要进行的操作,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL      删除;

fd:关联的文件描述符;

event:指向epoll_event的指针;

如果调用成功则返回0,不成功则返回-1。

(4) epoll_wait函数

函数声明:int epoll_wait(int epfd, structepoll_event * events, int maxevents, int timeout)

函数说明:该函数用于轮询I/O事件的发生。

epfd:由epoll_create 生成的epoll专用的文件描述符;

epoll_event:用于回传代处理事件的数组;

maxevents:每次能处理的事件数;

timeout:等待I/O事件发生的超时值;

返回发生事件数。

设计思路:

首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你的epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄,之后的所有操作都将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。

然后在你的网络主循环里���,调用epoll_wait(int epfd, epoll_event events, int max_events,int timeout)来查询所有的网络接口,看哪一个可以读,哪一个可以写。基本的语法为:

nfds = epoll_wait(kdpfd, events, maxevents, -1);

其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait函数操作成功之后,events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout参数指示 epoll_wait的超时条件,为0时表示马上返回;为-1时表示函数会一直等下去直到有事件返回;为任意正整数时表示等这么长的时间,如果一直没有事件,则会返回。一般情况下如果网络主循环是单线程的话,可以用-1来等待,这样可以保证一些效率,如果是和主循环在同一个线程的话,则可以用0来保证主循环的效率。epoll_wait返回之后,应该进入一个循环,以便遍历所有的事件。

对epoll 的操作就这么简单,总共不过4个API:epoll_create, epoll_ctl,epoll_wait和close。以下是man中的一个例子。

struct epoll_event ev, *events;

for(;;)

{

nfds = epoll_wait(kdpfd, events, maxevents, -1);    //等待IO事件

for(n = 0; n < nfds; ++n)

{

//如果是主socket的事件,则表示有新连接进入,需要进行新连接的处理。

if(events[n].data.fd == listener)

{

client = accept(listener, (struct sockaddr *) &local,  &addrlen);

if(client < 0)

{

perror("accept error");

continue;

}

// 将新连接置于非阻塞模式

setnonblocking(client);

ev.events = EPOLLIN | EPOLLET;

//注意这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听,

//如果有写操作的话,这个时候epoll是不会返回事件的,

//如果要对写操作也监听的话,应该是EPOLLIN | EPOLLOUT | EPOLLET。

// 并且将新连接也加入EPOLL的监听队列

ev.data.fd = client;

// 设置好event之后,将这个新的event通过epoll_ctl

if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0)

{

//加入到epoll的监听队列里,这里用EPOLL_CTL_ADD

//来加一个新的 epoll事件。可以通过EPOLL_CTL_DEL来减少

//一个epoll事件,通过EPOLL_CTL_MOD来改变一个事件的监听方式。

fprintf(stderr, "epoll set insertion error: fd=%d"0, client);

return -1;

}

}

else

// 如果不是主socket的事件的话,则代表这是一个用户的socket的事件,

// 则用来处理这个用户的socket的事情是,比如说read(fd,xxx)之类,或者一些其他的处理。

do_use_fd(events[n].data.fd);

}

}

7.EPOLL模型的一个实例

#include

#include

#include

#include

#include

#include

#include

#include

#define MAXLINE 10

#define OPEN_MAX 100

#define LISTENQ 20

#define SERV_PORT 5555

#define INFTIM 1000

void setnonblocking(int sock)

{

int opts;

opts = fcntl(sock, F_GETFL);

if(opts < 0)

{

perror("fcntl(sock, GETFL)");

exit(1);

}

opts = opts | O_NONBLOCK;

if(fcntl(sock, F_SETFL, opts) < 0)

{

perror("fcntl(sock,SETFL,opts)");

exit(1);

}

}

int main()

{

int i, maxi, listenfd, connfd, sockfd, epfd, nfds;

ssize_t n;

char line[MAXLINE];

socklen_t clilen;

//声明epoll_event结构体的变量, ev用于注册事件, events数组用于回传要处理的事件

struct epoll_event ev,events[20];

//生成用于处理accept的epoll专用的文件描述符, 指定生成描述符的最大范围为256

epfd = epoll_create(256);

struct sockaddr_in clientaddr;

struct sockaddr_in serveraddr;

listenfd = socket(AF_INET, SOCK_STREAM, 0);

setnonblocking(listenfd);       //把用于监听的socket设置为非阻塞方式

ev.data.fd = listenfd;          //设置与要处理的事件相关的文件描述符

ev.events = EPOLLIN | EPOLLET;  //设置要处理的事件类型

epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);//注册epoll事件

bzero(&serveraddr, sizeof(serveraddr));

serveraddr.sin_family = AF_INET;

char *local_addr = "200.200.200.204";

inet_aton(local_addr, &(serveraddr.sin_addr));

serveraddr.sin_port = htons(SERV_PORT);  //或者htons(SERV_PORT);

bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));

listen(listenfd, LISTENQ);

maxi = 0;

for( ; ; )

{

nfds = epoll_wait(epfd, events, 20, 500);//等待epoll事件的发生

for(i = 0; i < nfds; ++i)                 //处理所发生的所有事件

{

if(events[i].data.fd == listenfd)      //监听事件

{

connfd = accept(listenfd, (sockaddr *)&clientaddr, &clilen);

if(connfd < 0)

{

perror("connfd<0");

exit(1);

}

setnonblocking(connfd);           //把客户端的socket设置为非阻塞方式

char *str = inet_ntoa(clientaddr.sin_addr);

std::cout << "connect from " << str  <<:endl>

ev.data.fd=connfd;                //设置用于读操作的文件描述符

ev.events=EPOLLIN | EPOLLET;      //设置用于注测的读操作事件

epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);

//注册ev事件

}

else if(events[i].events&EPOLLIN)      //读事件

{

if ( (sockfd = events[i].data.fd) < 0)

{

continue;

}

if ( (n = read(sockfd, line, MAXLINE)) < 0) // 这里和IOCP不同

{

if (errno == ECONNRESET)

{

close(sockfd);

events[i].data.fd = -1;

}

else

{

std::cout<

}

}

else if (n == 0)

{

close(sockfd);

events[i].data.fd = -1;

}

ev.data.fd=sockfd;              //设置用于写操作的文件描述符

ev.events=EPOLLOUT | EPOLLET;   //设置用于注测的写操作事件

//修改sockfd上要处理的事件为EPOLLOUT

epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);

}

else if(events[i].events&EPOLLOUT)//写事件

{

sockfd = events[i].data.fd;

write(sockfd, line, n);

ev.data.fd = sockfd;               //设置用于读操作的文件描述符

ev.events = EPOLLIN | EPOLLET;     //设置用于注册的读操作事件

//修改sockfd上要处理的事件为EPOLIN

epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);

}

}

}

}

8.EPOLL进阶思考

8.1. 问题来源

最近学习EPOLL模型,介绍中说将EPOLL与Windows IOCP模型进行比较,说其的优势在于解决了IOCP模型大量线程上下文切换的开销,于是可以看出,EPOLL模型不需要多线程,即单线程中可以处理EPOLL逻辑。如果引入多线程反而会引起一些问题。但是EPOLL模型的服务器端到底可以不可以用多线程技术,如果可以,改怎么取舍,这成了困扰我的问题。上网查了一下,有这样几种声音:

(1) “要么事件驱动(如epoll),要么多线程,要么多进程,把这几个综合起来使用,感觉更加麻烦。”;

(2) “单线程使用epoll,但是不能发挥多核;多线程不用epoll。”;

(3) “主通信线程使用epoll所有需要监控的FD,有事件交给多线程去处理”;

(4) “既然用了epoll, 那么线程就不应该看到fd, 而只看到的是一个一个的业务请求/响应; epoll将网络数据组装成业务数据后, 转交给业务线程进行处理。这就是常说的半同步半异步”。

我比较赞同上述(3)、(4)中的观点

EPOLLOUT只有在缓冲区已经满了,不可以发送了,过了一会儿缓冲区中有空间了,就会触发EPOLLOUT,而且只触发一次。如果你编写的程序的网络IO不大,一次写入的数据不多的时候,通常都是epoll_wait立刻就会触发 EPOLLOUT;如果你不调用 epoll,直接写 socket,那么情况就取决于这个socket的缓冲区是不是足够了。如果缓冲区足够,那么写就成功。如果缓冲区不足,那么取决你的socket是不是阻塞的,要么阻塞到写完成,要么出错返回。所以EPOLLOUT事件具有较大的随机性,ET模式一般只用于EPOLLIN, 很少用于EPOLLOUT。

8.2. 具体做法

(1) 主通信线程使用epoll所有需要监控的FD,负责监控listenfd和connfd,这里只监听EPOLLIN事件,不监听EPOLLOUT事件;

(2) 一旦从Client收到了数据以后,将其构造成一个消息,放入消息队列中;

(3) 若干工作线程竞争,从消息队列中取出消息并进行处理,然后把处理结果发送给客户端。发送客户端的操作由工作线程完成。直接进行write。write到EAGAIN或EWOULDBLOCK后,线程循环continue等待缓冲区队列

发送函数代码如下:

bool send_data(int connfd, char *pbuffer, unsigned int &len,int flag)

{

if ((connfd < 0) || (0  == pbuffer))

{

return false;

}

int result = 0;

int remain_size = (int) len;

int send_size = 0;

const char *p = pbuffer;

time_t start_time = time(NULL);

int time_out = 3;

do

{

if (time(NULL) > start + time_out)

{

return false;

}

send_size = send(connfd, p, remain_size, flag);

if (nSentSize < 0)

{

if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))

{

continue;

}

else

{

len -= remain_size;

return false;

}

}

p += send_size;

remain_size -= send_size;

}while(remain_size > 0);

return true;

}

linux下epoll网络编程模型,C++ - 网络编程模型 - Linux EPOLL相关推荐

  1. Linux下使用Java调用Hikvision设备网络SDK使用指南

    1 简介  由于在开发过程中,本来以为抓图项目会部署在Windows服务器上,但随着项目的进行发现项目需要部署在Linux系统,甚至是国产化平台银河麒麟上,但在部署时发现在国产化平台部署时出现缺包的问 ...

  2. Linux下必须知道的11个网络命令

    Linux下必须知道的11个网络命令 作者:佚名 字体:[增加 减小] 来源:互联网 时间:03-06 15:54:29 我要评论 无论你是要下载文件.诊断网络问题.管理网络接口,还是查看网络的统计数 ...

  3. Linux下搭建第一个区块链网络(FISCO BCOS)

    Linux下搭建第一个区块链网络(FISCO BCOS) 概述 搭建单群组FISCO BCOS联盟链 配置及使用控制台 部署及调用HelloWorld合约 概述 FISCO BCOS是由国内企业主导研 ...

  4. linux下网易云音乐无法连接网络问题的解决思路

    linux下网易云音乐无法连接网络问题的解决思路 1.当所有需要联网的音乐播放器都出现网络无法连接或者google chrome 出现代理问题,可以检查下你最近是否弄了代理. 例如: 我最近就弄了个代 ...

  5. Linux下的静态链接库和动态链接库编程

    Linux下的静态链接库和动态链接库编程 参考: Linux下的静态链接库和动态链接库编程 - Histring - 博客园 Mac下静态库和动态库的创建和使用_C/C++_vincent2610的专 ...

  6. 南京邮电大学嵌入式系统开发实验5:嵌入式Linux下LED报警灯驱动设计及编程

    实验5  嵌入式Linux下LED报警灯驱动设计及编程 一.实验目的 理解驱动本质,掌握嵌入式Linux系统下驱动开发相关知识,包括端口寄存器访问.接口函数编写.和文件系统挂接.注册及相关应用编程等知 ...

  7. linux 服务器间通信,Linux 下的进程间通信:套接字和信号 | Linux 中国

    原标题:Linux 下的进程间通信:套接字和信号 | Linux 中国 学习在 Linux 中进程是如何与其他进程进行同步的. -- Marty Kalin 本篇是 Linux 下(IPC)系列的第三 ...

  8. Linux下查看、修改分区的卷标:修改linux分区的卷标

    Linux下查看.修改分区的卷标:修改linux分区的卷标可以用 e2label, 比如要把/dev/sda1 的卷标改为/boot,则可这样$ e2label /dev/sda1 /boot

  9. RHEL 5.4 下安装和使用 ntop 全纪录(ntop:Linux下可通过Web访问的网络流量监控工具)...

    RHEL 5.4 下安装和使用 ntop 全纪录 1. ntop介绍 Ntop是一种监控网络流量工具,用ntop显示网络的使用情况比其他一些网络管理软件更加直观.详细.Ntop甚至可以列出每个节点计算 ...

  10. linux下的mnt文件夹用来干嘛,Linux/mnt目录主要用于什么?

    linux下mnt目录的作用:mount 可直接理解为"挂载"挂接光驱.USB设备的目录,加载后,会在mnt里多出相应设备的目录.linux系统根目录下各个目录用途说明: 1./ ...

最新文章

  1. 在组策略中通过.zap的形式发布软件!
  2. docker 镜像名 tag 为none 的解决方案
  3. 财务需要学python-8年老财务:财务分析学python就能提高效率?一半人是骗子
  4. 1046. 划拳(15)
  5. 修改mysql编码方式
  6. Python+matplotlib一笔绘制红色五角星
  7. java 字面值_Java基础之字面值
  8. 哈希表实现电话号码查找系统
  9. 基于C#窗体的酒店管理系统
  10. EXCEL生成MySql建表语句
  11. Leetcode——226. 翻转二叉树
  12. 百度地图之一百度地图搭建开发环境(老罗视频笔记整理)
  13. 卖家中心html素材,天猫素材中心入口和使用方法
  14. git 查看自己秘钥_git生成和检查秘钥操作
  15. 从冷备到多活,阿里毕玄谈数据中心的异地容灾
  16. java ajax教程_AJAX Java
  17. 第十一章 曲面积分与曲线积分(同济高等数学A)
  18. 诺基亚裁员背水一战:腹背受敌押宝WP手机
  19. 下载Microsoft Store的应用安装包
  20. Koreographer音乐节奏插件使用教程

热门文章

  1. c#怎么拟合函数得到参数_c#怎么拟合函数得到参数_最小二乘法拟合任意次曲线(C#)...
  2. Cesium:显示一个3D地球
  3. linux 执行安装脚本,批量执行Linux安装程序和脚本
  4. 解决磁盘IO紧张的一种方法
  5. 深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST
  6. 点云质量评估_Chamfer Distance--倒角距离
  7. Matlab使用for循环将多个行向量合成一个行向量或者一个多维矩阵
  8. QT练习5:显示GIF图片
  9. Java基础语法实例(2)——实习第二天
  10. php的基本语法与字符串与增删改查