多路IO之epoll边沿触发+非阻塞

  1. 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_ET_LT_NOBLOCK_example
  2. 源码说明:
    • server.cpp: 监听127.1:6666,功能是将收到的数据打印到屏幕

1. 概要

这里没有用socket文件描述符,而是用了管道,关于管道的知识可以参考: https://www.bilibili.com/video/av41308301?p=13。

//设置边沿触发
evt.data.fd = pfd[0];
evt.events = EPOLLIN | EPOLLET; //边沿
// evt.events = EPOLLIN; //水平(默认)
epoll_ctl(epfd, EPOLL_CTL_ADD, pfd[0], &evt); //设置非阻塞
int flag = 0;
flag = fcntl(pfd[0], F_GETFL);
flag |= O_NONBLOCK; //设置为非阻塞
fcntl(pfd[0], F_SETFL, flag); //后面也有直接用以下设置的方法
fcntl(pfd[0], F_SETFL, O_NONBLOCK);

1.1 边沿触发和水平触发

假设一个情景: 接收了100字节的数据到fd的接收缓冲区, epoll_wait返回,说fd有数据,于是开始处理,但是只从缓冲区中read了10字节(所以缓冲区中还有90字节), 那么下一次调用epoll_wait时(假设fd没有新的数据到),是否还要返回fd有信号?

  • 边沿触发: 不返回了, 只有新数据来才返回
  • 水平触发: 仍然返回, 直接缓冲区还有数据就返回

1.2 边沿触发的优势缺点

优势:

  1. 不会像水平触发傻瓜式一直触发epoll_wait返回
  2. 边沿触发自定义更方便,有新数据了告诉我,至于我是否处理了怎么处理都不用管,当然这种难度相对大

缺点: 万一没有处理完数据,就会出问题了

1.3 边沿触发缺点的改进方法

int flag = 0;
flag = fcntl(pfd[0], F_GETFL);
flag |= O_NONBLOCK; //设置为非阻塞
fcntl(pfd[0], F_SETFL, flag); while ((len = (int)read(pfd[0], buf, 3)) > 0) { //体现非阻塞
write(STDOUT_FILENO, buf, (size_t)len);
}

while read>0确保read完所有数据, 因为在非阻塞模式下,如果缓冲区中没有数据了read也会返回, 关于read在非阻塞的返回值可以搜索.

ssize_t Read(int fildes, void *buf, size_t nbyte) {
readagain:
ssize_t ret = read(fildes, buf, nbyte);
if (ret == -1) {
if (errno == EINTR) {
goto readagain;
} else if (errno == EWOULDBLOCK || errno == EAGAIN) { //在非阻塞模式下,这个是正常的,表示没数据可读了
return ret;
} else {
perror_exit("read failed");
}
}
return ret;
}

2. 核心代码

这是用另一种文件描述符证明.

#include "include/wrap.h"
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <unistd.h> int main() {
int pfd[2];
int pid = 0;
char buf[3] = {0};
char buf1[3] = {0};
int len = 0; pipe(pfd);
pid = fork(); if (pid == -1) { // error
perror_exit("fork failed");
} if (pid == 0) { // child
close(pfd[0]);
int j = 0;
while (true) {
sprintf(buf1, "%d\n", j++);
write(pfd[1], buf1, 3);
sprintf(buf1, "%d\n", j++);
write(pfd[1], buf1, 3);
sleep(3);
}
close(pfd[1]);
} else { // parent
close(pfd[1]);
int epfd = 0;
struct epoll_event evts[1];
struct epoll_event evt; int flag = 0;
flag = fcntl(pfd[0], F_GETFL);
flag |= O_NONBLOCK; //设置为非阻塞
fcntl(pfd[0], F_SETFL, flag); epfd = epoll_create(1);
evt.data.fd = pfd[0];
evt.events = EPOLLIN | EPOLLET; //边沿
// evt.events = EPOLLIN; //水平(默认)
epoll_ctl(epfd, EPOLL_CTL_ADD, pfd[0], &evt);
while (true) {
epoll_wait(epfd, evts, 1, -1);
if (evts[0].data.fd == pfd[0]) {
while ((len = (int)read(pfd[0], buf, 3)) > 0) { //体现非阻塞
write(STDOUT_FILENO, buf, (size_t)len);
}
}
}
close(pfd[0]);
} return 0;
}
  • 水平触发: (不设置不阻塞, 用EPOLLIN, 不用while read),需要调用两次epoll_wait
  • 边沿触发: 只调用一次epoll_wait

3. 参考网址

  1. https://www.bilibili.com/video/av53016117?p=66
  2. https://www.bilibili.com/video/av41308301?p=13

Linux C++ 网络编程学习系列(5)——多路IO之epoll边沿触发的更多相关文章

  1. Linux C++ 网络编程学习系列(1)——端口复用实现

    Linux C++ 网络编程学习系列(1)--端口复用实现 源码地址:https://github.com/whuwzp/linuxc/tree/master/portreuse 源码说明: serv ...

  2. Linux C++ 网络编程学习系列(6)——多路IO之epoll高级用法

    poll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_libevent 源码说明: server.cpp: 监听127. ...

  3. Linux C++ 网络编程学习系列(4)——多路IO之epoll基础

    epoll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll 源码说明: server.cpp: 监听127.1:6666,功 ...

  4. Linux C++ 网络编程学习系列(3)——多路IO之poll实现

    poll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/poll 源码说明: server.cpp: 监听127.1:6666,功能是 ...

  5. Linux C++ 网络编程学习系列(2)——多路IO之select实现

    select实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/select 源码说明: server.cpp: 监听127.1:6666 ...

  6. Linux C++ 网络编程学习系列(7)——mbedtls编译使用

    mbedtls编译使用 环境: Ubuntu18.04 编译器:gcc或clang 编译选项: 静态编译使用 1. mbedtls源码 下载地址: https://github.com/ARMmbed ...

  7. Linux C网络编程学习笔记

    Linux C网络编程总结报告 一.Linux C 网络编程知识介绍: 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端:(client) 在网络程序中, ...

  8. linux下网络编程学习——入门实例ZZ

    http://www.cppblog.com/cuijixin/archive/2008/03/14/44480.html 是不是还对用c怎么实现网络编程感到神秘莫测阿,我们这里就要撕开它神秘的面纱, ...

  9. Linux下网络编程学习杂记

    1.TCP/IP协议的体系结构包含四层:应用层(负责应用程序的网络服务,通过端口号识别各个不同的进程)->传输层(传输控制层协议TCP.用户数据报协议UDP.互联网控制消息协议ICMP)-> ...

随机推荐

  1. pytorch里一些函数的说明记录

    THLongStorage *THTensor_(newSizeOf)(THTensor *self); THLongStorage *THTensor_(newStrideOf)(THTensor ...

  2. Vulnhub靶场DC-1 WP

    前言 之前提到过最近在做vlunhub的靶场复现工作,今天开始更新writeup吧.(对着walkthrough一顿乱抄嘻嘻嘻) 关于DC-1(官网翻译来的) 描述 DC-1是一个专门构建的易受攻击的 ...

  3. Jmeter Agent自动化

    1.打开菜单栏-附件-系统工具-任务计划程序,新建PerformanceTest目录. 2.在PerformanceTest目录下新建一个基本任务. 3.完成. 这样,当我们在使用Jmeter进行分布 ...

  4. github的上面的安全工具

    简介 Scanners Box也被称为 scanbox,是一个强大完备的黑客工具箱,它收集了Github上数10种类别的开源扫描器,包括子域名,数据库,中间件和其他模块化设计的扫描器等,但对于一些被大 ...

  5. MySQL:GROUP_CONCAT函数的使用

    原文链接 GROUP_CONCAT功能 将某个字段的值拼接成字符串. 举例使用 先看一下原始数据表 执行下面sql语句 SELECT `cid`,GROUP_CONCAT(mid) AS `mids` ...

  6. editplus软件使用技巧

    1.文本文件的特点 注:不针对editplus这个软件,对于其他的文本文件处理软件也同样适用. 文本文件就是不包含其它文字格式(比如字体,字号,对齐,行间距等)以及富文本(比如图片,表格等)的一种纯文 ...

  7. React Hook上车

    React Hook 是 v16.8 的新功能,自诞生以来,受到广泛的好评,在 React 版本更新中具有里程碑的意义.现在都2020年了,再不上车 React Hook 就真的 out 了... H ...

  8. Nginx Rewrite规则的break和last示例

    break和last各自的作用 官方解释 last:stops processing the current set of ngx_http_rewrite_module directives fol ...

  9. 基于MVP模式实现四则运算器

    基于MVP模式四则运算器 来到新东家,项目的框架采用的是MVP模式,刚来公司的时候,项目经理给予分配小任务,首先熟悉MVP模式,而后普通的四则运算器的实现使用MVP分层.这里主要回顾当时做任务时候的对 ...

  10. 树莓派 Raspberry PI之GPIO

    树莓派 Raspberry PI之GPIO 树莓派各版本硬件原理图:https://www.raspberrypi.org/documentation/hardware/raspberrypi/REA ...