自学笔记,没有历史知识铺垫(省略百度部分)C语言笔记-27-网络-多路复用epoll
注意 epoll在linux、unix中存在,macOS中没有,平替函数kqueue
#include
// 创建一个epoll实例,返回该实例的文件描述符.close()可以关闭文件描述符.
int epoll_create(int size);
参数
size
实例大小(必须大于0)
返回值
成功 文件描述符
错误 -1 errno被设置
#include
// 控制epoll文件描述符
int epoll_ctl(int epfd,int op, int fd, struct epoll_event *event);
参数
epfd
指定了要操作的epoll设备的文件描述符
op
改变fd相关事件
EPOLL_CTL_ADD
新增fd相关的事件到epoll实例EPOLL_CTL_MOD
修改epoll实例中的fd相关的事件EPOLL_CTL_DEL
删除epoll实例中的fd
fd
指定了要代理的文件描述符
event
指定了跟fd相关的事件struct epoll_event { uint32_t events; /* Epoll events. EPOLLIN 读操作事件、EPOLLOUT 写操作事件 */ epoll_data_t data; /* User data variable */ }; typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t;
返回值
成功 0
错误 -1 errno被设置
#include
// 等待epoll实例代理检测的I/O事件
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
参数
epfd
指定epoll实例
events
将检测到的事件保存到events中
maxevents
设置最大检测事件数
timeout
设置等待的毫秒数(-1 一直等待到有事件)
返回值
成功 返回可用的文件描述符的最大个数(如果指定时间内没有I/O请求,0)
错误 -1 errno被设置
#include
#include
#include
#include
#include
#include
#include
#include
// 校验错误工具
void check_err(int fd, char *title)
{
if (fd == -1)
{
perror(title);
}
}
// 启动服务函数
int start_server(short port)
{
printf("服务器端启动中...\n");
// 使用sockaddr_in 替换 sockaddr
struct sockaddr_in server_addr;
// 家族簇
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 创建socket设备
int sfd = socket(PF_INET, SOCK_STREAM, 0);
check_err(sfd, "创建socket设备失败");
// 绑定socket设备到ip、端口
int bindResult = bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
check_err(bindResult, "绑定socket设备失败");
// 监听客户端消息到 链接未决队列
int listenResult = listen(sfd, 10);
check_err(listenResult, "监听socket设备失败");
printf("服务器端启动成功,开始监听客户端消息...\n");
return sfd;
}
int main(int argc, char *argv[])
{
// 启动服务
int sfd = start_server(8089);
// 1. 创建容量为2的epoll实例
int efd = epoll_create(2);
check_err(efd, "创建epoll实例失败");
// 2. 将socket服务添加到epoll实例中,且指定模式为只读
struct epoll_event e_event;
// 只读
e_event.events = EPOLLIN;
// tcp服务端文件描述符
e_event.data.fd = sfd;
// 3. 控制epoll文件描述符 将sfd以e_event事件,加入到epoll实例中
int ctl_result = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &e_event);
check_err(ctl_result, "将socket服务添加到epoll实例失败");
// 创建未决链接事件、IO事件容器
struct epoll_event ew_event[100];
while (1)
{
// 4. 等待检测epoll代理事件发生(此处存放未决链接事件和IO事件) 将接收的事件放入指定事件数量容器,且指定同时存储最大事件数
int ewfd = epoll_wait(efd, ew_event, 90, -1);
check_err(ewfd, "等待epoll链接失败");
// 循环epoll事件容器 0时表示没有事件
for (int i = 0; i < ewfd; i++)
{
// 链接事件描述符时,表示未决链接事件发生
if (ew_event[i].data.fd == sfd)
{
// 从链接未决队列中取出未决链接
int accept_result = accept(sfd, NULL, NULL);
check_err(accept_result, "接收客户端消息失败");
// 5. 将客户端链接的io读操作存入epoll实例
e_event.events = EPOLLIN;
e_event.data.fd = accept_result;
int ctl_result2 = epoll_ctl(efd, EPOLL_CTL_ADD, accept_result, &e_event);
check_err(ctl_result2, "将accept_result加到epoll实例失败");
}
else
{
// 客户端io读操作(在第5步加入到epoll进行管理的事件)
char buf[128];
// 读取客户端消息
int r = read(ew_event[i].data.fd, buf, 128);
// 处理客户端消息,返回给客户端
write(ew_event[i].data.fd, buf, r);
// 写入控制台展示
write(1, buf, r);
// 6. 移除客户端io读操作事件
int ctl_result3 = epoll_ctl(efd, EPOLL_CTL_DEL, ew_event[i].data.fd, &e_event);
check_err(ctl_result3, "将accept_result从epoll实例移除失败");
// 结束客户端IO读写操作
close(ew_event[i].data.fd);
}
}
}
return 0;
}
本章主要为C语言笔记-27-网络-多路复用epoll