27.epoll(三)

一.回顾

建议先学上篇博客,再向下学习,上篇博客的链接如下:

https://blog.csdn.net/weixin_60668256/article/details/155077409?fromshare=blogdetail&sharetype=blogdetail&sharerId=155077409&sharerefer=PC&sharesource=weixin_60668256&sharefrom=from_link

二.IOService.hpp的sender()实现

cpp 复制代码
virtual void Sender() override
    {
        while(true)
        {
            ssize_t n = send(sockfd(),OutString().c_str(),OutString().size(),0);
            if(n > 0)
            {
                //成功
                DisCardOutString(n);//移除发送的n个字符串
            }
            else if(n == 0)
            {
                break;
            }
            else
            {
                if(errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    //缓冲区写满了,下次再来写
                    break;
                }
                else if(errno == EINTR)
                {
                    continue;
                }
                else
                {
                    Excepter();
                    return;
                }
            }
        }
        //本轮发送完了
        //1.outbuffer empty
        //2.发送缓冲区写满了 && outbuffer 没有empty
        //写条件不满足,我们要将sockfd的事件写入到epoll模型中
        if(!IsOutBufferEmpty())
        {
            //修改对sockfd的事件关心!  --  开启对写事件的关心
            GetOwner()->EnableReadWrite(sockfd(),true,true);
        }
        else
        {
            GetOwner()->EnableReadWrite(sockfd(),true,false);
        }
    }

我们要将给文件描述符的关心事件全部都进行更改

cpp 复制代码
void EnableReadWrite(int sockfd,bool readable,bool writeable)
    {
        if(IsConnectionExists(sockfd))
        {
            uint32_t events = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
           _connections[sockfd]->SetEvents(events);

           _epoller->Update(sockfd,_connections[sockfd]->GetEvents());
        }
    }
cpp 复制代码
void Ctrl(int sockfd,uint32_t events,int flag)
        {
            struct epoll_event ev;
            ev.events = events;
            ev.data.fd = sockfd;
            int n = epoll_ctl(_epfd,flag,sockfd,&ev);
            if(n < 0)
            {
                LOG(LogLevel::WARNING) << "epoll_ctl error";
            }
        }
        void Add(int sockfd,uint32_t events)
        {
            Ctrl(sockfd,events,EPOLL_CTL_ADD);
        }
        void Update(int sockfd,uint32_t events)
        {
            Ctrl(sockfd,events,EPOLL_CTL_MOD);
        }

我可以用以前的客户端来向我们的服务器进行发起请求

三.异常处理

cpp 复制代码
virtual void Excepter()override
    {
        //IO读取时,所有的异常处理,全部都会转化为这个函数
        //打印日志,差错处理,关闭连接,Reactor移除connection,删除epoll模型中的节点
        LOG(LogLevel::INFO) << "客户端可能结束,进行异常处理: " << sockfd();
        GetOwner()->DelConnection(sockfd());
    }

四.后续扩展设计

每次发送消息和接收消息,都进行更新时间(这样我们就能做连接管理了)

五.多进程和多线程

1.方案一(多进程)

由一个Master创建多个子进程,然后用管道进行通信

有任何一个请求到了,Master进程不做任何的处理,直接通知子进程

epoll啥都能进行关心(包括管道)

2.方案二(多线程)

我们使用管道,既可以唤醒对应的Reactor,也可以唤醒对应的条件变量

3.方案三(事件驱动)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/eventfd.h>

int main() {
    int efd = eventfd(0, EFD_CLOEXEC);  // 创建 eventfd
    if (efd == -1) {
        perror("eventfd");
        return 1;
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {  // 子进程
        uint64_t value;
        read(efd, &value, sizeof(value));  // 从 eventfd 读取事件
        printf("Child process received event\n");
        close(efd);
        return 0;
    } else {  // 父进程
        uint64_t value = 1;
        write(efd, &value, sizeof(value));  // 向 eventfd 写入事件
        printf("Parent process sent event\n");
        wait(NULL);  // 等待子进程结束
        close(efd);
        return 0;
    }
}

我们这里使用的是用户空间用于我们文件描述符的传递,事件通知用我们对应的eventfd

相关推荐
兩尛3 小时前
c++知识点2
开发语言·c++
fengfuyao9853 小时前
海浪PM谱及波形的Matlab仿真实现
开发语言·matlab
xiaoye-duck4 小时前
C++ string 底层原理深度解析 + 模拟实现(下)——面试 / 开发都适用
开发语言·c++·stl
汤愈韬4 小时前
ACL概述、ACL原理、基本ACL应用及配置
网络·网络协议·网络安全
Hx_Ma165 小时前
SpringMVC框架提供的转发和重定向
java·开发语言·servlet
期待のcode5 小时前
原子操作类LongAdder
java·开发语言
小二李6 小时前
第11章 nestjs服务端开发:登录鉴权
运维·服务器
lly2024066 小时前
C 语言中的结构体
开发语言
JAVA+C语言6 小时前
如何优化 Java 多主机通信的性能?
java·开发语言·php
i建模7 小时前
如何在Arch Linux中重设忘记的root密码
linux·运维·服务器