一.回顾
建议先学上篇博客,再向下学习,上篇博客的链接如下:
二.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
