多路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. 云CRM和本地CRM哪个更好

    现在CRM系统按照服务器部署方式的不同分为本地CRM和云CRM两种,本地CRM需要在企业内部部署服务器,而云CRM的服务器则是安装在云服务器上面,很多企业可能不知道应该选择云CRM还是本地CRM.下面 ...

  2. 解开SQL注入的神秘面纱-来自于宋沄剑的分享

    解开SQL注入的神秘面纱-来自于宋沄剑的分享 https://files.cnblogs.com/files/wxlevel/揭开SQL注入的神秘面纱.pdf

  3. python的进制转换

    转载于:https://www.cnblogs.com/FWF1944/p/11132409.html(方法论190404) Python整数能够以十六进制,八进制和二进制来编写,作为一般以10位基数 ...

  4. DRF之APIView源码简析

    一. 安装djangorestframework 安装的方式有以下三种,注意,模块就叫djangorestframework. 方式一:pip3 install djangorestframework ...

  5. Django 缓存机制

    一 缓存介绍 在动态网站中,用户所有的请求,服务器都会去数据库中进行相应的增,删,查,改,渲染模板,执行业务逻辑,最后生成用户看到的页面. 当一个网站的用户访问量很大的时候,每一次的的后台操作,都会消 ...

  6. java触发full gc的几种情况概述

    前言 近期被问及这个问题,在此记录整理一下. System.gc()方法的调用 此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full ...

  7. Chrome80调整SameSite策略对IdentityServer4的影响以及处理方案(翻译)

    首先,好消息是Goole将于2020年2月份发布Chrome 80版本.本次发布将推进Google的"渐进改良Cookie"策略,打造一个更为安全和保障用户隐私的网络环境. 坏消息 ...

  8. macOS 去掉系统软件更新红点提示

    当前系统提示更新到macOS Catalina .打开终端执行以下命令: 第一步运行: sudo softwareupdate --ignore "macOS Catalina" ...

  9. 全屏banner及全屏轮播

    一.全屏banner 1.设置网页图片全屏banner <!DOCTYPE html> <html lang="en"> <head> < ...

  10. 将java中Map对象转为有相同属性的类对象(json作为中间转换)

    java中Map对象转为有相同属性的类对象(json作为中间转换) 准备好json转换工具类 public class JsonUtil { private static ObjectMapper o ...