------------->【Linux系统编程/网络编程】(学习目录汇总) <--------------
1.epoll 反应堆模型:
实际上就是使用epoll的ET模式 + 非阻塞、轮询 + void *ptr(设置回调函数)。
接下来我们回顾一下之前用epoll实现的并发服务器
先使用 socket()、bind()、listen()创建套接字并初始化 --> epoll_create() 创建监听红黑树 --> 返回 epfd --> epoll_ctl() 向树上添加1个监听fd --> while(1)循环–> epoll_wait() 监听 --> 对应监听fd有事件产生 --> 返回监听满足数组。 --> 判断返回数组元素 --> 若lfd满足 --> 则调用Accept() -->若 cfd 满足 -->则调用 read() —> 小写转大写 --> write回去。
接下来再看看用epoll反应堆实现的并发服务器工作流程,实际上不同的是,在监听cfd过程中,不仅仅要监听 cfd 的读事件、还要监听cfd的写事件,并且分别对它们读写事件设置各自的回调函数。当各自对应的事件满足时,直接调用满足该事件的回调函数。
先使用socket()、bind()、listen()创建套接字并初始化 --> epoll_create() 创建监听红黑树 --> 返回 epfd --> epoll_ctl() 向树上添加一个监听fd --> while(1)循环 --> epoll_wait() 监听 --> 对应监听fd有事件产生 --> 返回监听满足数组。 --> 判断返回数组元素 -->若 lfd满足 --> Accept() -->若 cfd 满足 --> read() —> 小写转大写 --> cfd从监听红黑树上摘下 --> 设置EPOLLOUT监听写事件,设置回调函数 --> 调用epoll_ctl() 设置EPOLL_CTL_ADD 重新放到红黑上监听写事件 --> 等待 epoll_wait 返回 --> 说明 cfd 可写 --> write回去–> cfd从监听红黑树上摘下 -->设置 EPOLLIN监听读事件,设置回调函数 --> epoll_ctl()设置EPOLL_CTL_ADD 重新放到红黑上监听读事件 --> epoll_wait() 监听
反应堆的理解:加入IO转接之后,有了事件,server才去处理,这里反应堆也是这样,由于网络环境复杂,服务器处理数据之后,可能并不能直接写回去,比如遇到网络繁忙或者对方缓冲区已经满了这种情况,就不能直接写回给客户端。反应堆就是在处理数据之后,监听写事件,能写回给客户端了,才去做写回操作。写回之后,再改为监听读事件。如此循环。
大家可以结合这个视频p284集进行理解
2.epoll反应堆代码实现
接下来直接看到代码吧:
/*************************************************************************
# > File Name:server.c
# > Author: Jay
# > Mail: billysturate@gmail.com
# > Created Time: Sun 23 Oct 2022 05:36:01 PM CST
************************************************************************/
#include <stdio.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define MAX_EVENTS 1024 //监听上限数
#define BUFLEN 4096
#define SERV_PORT 8080
void recvdata(int fd, int events, void *arg);
void senddata(int fd, int events, void *arg);
/* 描述就绪文件描述符相关信息 */
struct myevent_s {
int fd; //要监听的文件描述符
int events; //对应的监听事件
void *arg; //泛型参数
void (*call_back)(int fd, int events, void *arg); //回调函数
int status; //是否在监听:1->在红黑树上(监听), 0->不在(不监听)
char buf[BUFLEN];
int len;
long last_active; //记录每次加入红黑树 g_efd 的时间值
};
int g_efd; //全局变量, 保存epoll_create返回的文件描述符
struct myevent_s g_events[MAX_EVENTS+1]; //自定义结构体类型数组. +1-->listen fd
/*将结构体 myevent_s 成员变量 初始化*/
void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg)
{
ev->fd = fd;
ev->call_back = call_back;
ev->events = 0;
ev->arg = arg;
ev->status = 0;
memset(ev->buf, 0, sizeof(ev->buf));
ev