一.epoll函数解析
其本质是红黑树和链表
-
int epoll_create(int size);
作用:创建一个epoll红黑树
size:创建的红黑树的监听节点数量(仅供内核参考)
返回值:
成功–返回新创建的红黑树的根节点的fd;失败 -1 error -
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
作用:对epoll进行操作,ADD,MOD,DEL等
epfd:epoll_create函数的返回值 epfd
fd:待监听的fd
op:对该监听红黑树所要做的操作
EPOLL_CTL_ADD 添加fd到监听红黑树
EPOLL_CTL_MOD 修改fd在监听红黑树上的监听事件
EPOLL_CTL_DEL 将一个fd从监听红黑树上摘下(取消监听)
event:本质是 struct epoll_event 结构体地址
event结构体成员:
(1)events:EPOLLIN、EPOLLOUT、EPOLLERR
(2)data:联合体(内部: int fd:对应监听事件的fd ,void *ptr:泛型指针 )
返回值:
成功0,失败-1 error
3. int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
作用:监听红黑树上节点的对应事件,以events作为传出参数,返回所有被监听到的文件描述符数组。
epfd:epoll_create函数的返回值 epfd
events:传出参数,一个数组[],用于存放满足监听条件的那些fd结构体
maxevents:数组元素的总个数 struct epoll_event events[1024]
timeout:
-1:阻塞
0:不阻塞
>0:超时时间(毫秒)
返回值:
>0:满足监听的总个数,可以用作循环上限
0:没有fd满足监听的事件
-1:失败,error

二.epoll实现多路IO转接
核心思路
- socket()、bind()、listen() 初始化socket,返回lfd(监听所用socket)
- epoll_create创建红黑树,它的返回值就是树的根节点
- epoll_ctl将listenfd添加到树上
- 循环epoll_wait进行监听,它的返回值是满足监听的总个数,所以以它的返回值为遍历上限去判断事件
- 如果它返回的数组中data.fd等于lfd,那么就accept去连接客户端 并将新的cfd加入树中
- 如果不是lfd,就说明有读事件发生,就去判断读到的返回值,<0是出错 ==0是客户端关闭(这两个都要去将该cfd从树中移除 并close),>0就处理数据然后写回
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define OPEN_MAX 5000
#define SERVE_PORT 9527
int main()
{
// 所需要的变量
int lfd, cfd, efd, ret, wait_ret, i, sockfd, len;
char buf[1024];
// 地址结构体
struct sockaddr_in serve_addr, client_addr;
socklen_t client_addr_len;
serve_addr.sin_family = AF_INET; // IPV4
serve_addr.sin_port = htons(SERVE_PORT); // 绑定端口
serve_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定ip(ANY系统自动分配)
// 创建socket
lfd = socket(AF_INET, SOCK_STREAM, 0);
if (lfd < 0)
{
perror("socket error");
exit(1);
}
// bind绑定
bind(lfd, (struct sockaddr *)&serve_addr, sizeof(serve_addr));
// 设置上限
listen(lfd, 128);
// 创建红黑树
efd = epoll_create(1); // efd就是树的根节点
// 将lfd挂在树上
// epoll结构体 ep是epoll_wait所需的数组(存放满足事件的fd)
struct epoll_event tep, ep[128]; // tep是epoll_ctl的参数(传监听的事件)
tep.events = EPOLLIN;
tep.data.fd = lfd;
ret = epoll_ctl(efd, EPOLL_CTL_ADD, lfd, &tep);
if (ret < 0)
{
perror("epoll_ctl error");
exit(1);
}
// 循环去epoll_wait进行监听
while (1)
{
wait_ret = epoll_wait(efd, ep, 128, -1); // wait_ret就是实际满足事件的总个数
// 以wait_ret为上限去遍历事件
for (i = 0; i < wait_ret; i++)
{
// sockfd用于接收满足事件的fd
sockfd = ep[i].data.fd;
// 如果等于lfd,那就说明有客户端要来连接,就去accept
if (sockfd == lfd)
{
client_addr_len = sizeof(client_addr);
cfd = accept(lfd, (struct sockaddr *)&client_addr, &client_addr_len);
// 将cfd设置为非阻塞
int flag = fcntl(cfd, F_GETFL);
flag |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flag);
// 把新的cfd加入树中
tep.events = EPOLLIN | EPOLLET;
tep.data.fd = cfd;
ret = epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &tep);
if (ret < 0)
{
perror("epoll_ctl cfd error");
exit(1);
}
}
// 如果不是lfd,那就说明有读事件发生(读数据)
else
{
len = read(sockfd, buf, sizeof(buf));
if (len == 0) // 说明对方关闭连接(从树上摘下 & close)
{
epoll_ctl

最低0.47元/天 解锁文章
2192

被折叠的 条评论
为什么被折叠?



