zoukankan      html  css  js  c++  java
  • Linux C++ 网络编程学习系列(5)——多路IO之epoll边沿触发

    多路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
    ", j++);
                write(pfd[1], buf1, 3);
                sprintf(buf1, "%d
    ", 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
  • 相关阅读:
    Eclipse+Maven整合开发Java项目(二)➣webapp3.0以上的Maven项目
    Eclipse+Maven整合开发Java项目(一)➣Maven基础环境配置
    JAVA-ORM框架整理➣Mybatis操作MySQL
    Spring MVC基础知识整理➣Spring+SpringMVC+Hibernate整合操作数据库
    Spring MVC基础知识整理➣拦截器和自定义注解
    Spring MVC基础知识整理➣国际化和异常处理
    Spring MVC基础知识整理➣数据校验与格式化
    Spring MVC基础知识整理➣View与Controller数据交互
    面向过程,面向对象,函数式对同一个问题的思考方式
    分享一个简单程序(webApi+castle+Automapper+Ef+angular)
  • 原文地址:https://www.cnblogs.com/whuwzp/p/linux_cpp_network_4_epoll_EPOLLET.html
Copyright © 2011-2022 走看看