epoll事件模型: ET(高速模式)(边沿触发)下
1、fd先设置非阻塞属性
int flags = fcntl(wevent[i].data.fd, F_GETFL);
flags |= O_NONBLOCK;
ret = fcntl(wevent[i].data.fd, F_SETFL, flags);
2、再把fd加入监听树中
// cfd先设置非阻塞属性
int flags = fcntl(wevent[i].data.fd, F_GETFL);
flags |= O_NONBLOCK;
ret = fcntl(wevent[i].data.fd, F_SETFL, flags);
// 新cfd加入监听树(监听读事件)
struct epoll_event tmp;
event.data.fd = cfd;
event.events = EPOLLIN | EPOLLET; // 改为边沿触发ET(监听的fd中数据到达的一刻才会触发epoll_wait)
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &event);
#include "wrap.h"
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <fcntl.h>
#define PORT 9527
#define LISTENMAX 128
#define TESTBUF 5
int main(void)
{
// 1、socket()创建监听套接字
int lfd, cfd;
int ret;
int n; // 读到的字节数
char buf[TESTBUF]; // 用来接收客户端发来的数据
lfd = Socket(AF_INET, SOCK_STREAM, 0);
// 2、setsockopt设置端口复用
int opt = 1;
ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
if (ret == -1)
sys_error("setsockopt error");
// 3、bind()绑定地址结构ip+端口号
struct sockaddr_in saddr;
socklen_t saddrlen = sizeof(saddr);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT); // 网络字节序下的端口号
saddr.sin_addr.s_addr = htonl(INADDR_ANY); // 网络字节序下的ip地址
Bind(lfd, (struct sockaddr *)&saddr, saddrlen);
struct sockaddr_in caddr; // 用来接收建立连接的客户端地址结构
socklen_t caddrlen = sizeof(caddr);
// 4、listen设置监听上限
Listen(lfd, LISTENMAX);
// 5、epoll小秘书只负责监听(1次调用, 只监听1次)
// 6、epoll_create创建1棵监听树
int epfd; // 监听树的根结点
epfd = epoll_create(128); // 仅供参考, 监听树的结点数128 内核会扩容的
if (epfd == -1)
sys_error("epoll_create error");
// 7、epoll_ctl往监听树上添加结点(想要监听的客户端=文件描述符)
// 初始: 只监听lfd的读事件 = 把lfd加入监听树
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = lfd;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &event);
if (ret == -1)
sys_error("epoll_ctl error");
int epnum; // 接收监听树中所有的事件数
struct epoll_event wevent[BUFSIZ]; // (传出参数)接收监听树中实际发生读事件的结点
while (1)
{
// 5、epoll_wait小秘书只负责监听
epnum = epoll_wait(epfd, wevent, BUFSIZ, 0); // 采用非阻塞轮询
// 8、分析一次监听中产生的读事件
if (epnum == -1)
sys_error("epoll_wait error");
else if (epnum > 0) // 有读事件产生
{
// 传出的数组-挨个看看是谁产生的读事件
for (int i = 0; i < epnum; ++i)
{
if (wevent[i].data.fd == lfd)
{
// 9、lfd有读事件-新客户端建立连接
cfd = Accept(lfd, (struct sockaddr *)&caddr, &caddrlen);
// 打印客户端信息
char cip[256];
inet_ntop(AF_INET, &caddr.sin_addr.s_addr, cip, sizeof(cip));
printf("新连接的客户端: ip %s port %d\n", cip, ntohs(caddr.sin_port));
// cfd设置非阻塞属性
int flags = fcntl(wevent[i].data.fd, F_GETFL);
if (flags == -1)
sys_error("fcntl error 1");
flags |= O_NONBLOCK;
ret = fcntl(wevent[i].data.fd, F_SETFL, flags);
if (ret == -1)
sys_error("fcntl error 2");
// 10、新cfd加入监听树(监听读事件)
event.data.fd = cfd;
// event.events = EPOLLIN; // 默认水平触发LT
event.events = EPOLLIN | EPOLLET; // 改为边沿触发ET(监听的fd中数据到达的一刻才会触发epoll_wait)
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &event);
if (ret == -1)
sys_error("while epoll_ctl error");
}
else
{
// 9、其他fd有读事件-旧客户端想发数据给我
while ((n = read(wevent[i].data.fd, buf, TESTBUF)) > 0) // 忙轮询
{
// 10、模拟数据处理
for (int j = 0; j < n; ++j)
buf[j] = toupper(buf[j]);
// 11、发给客户端
write(wevent[i].data.fd, buf, n);
}
}
}
}
}
Close(lfd);
return 0;
}
nc 127.0.0.1 9527测试(read一次只读5个字节)高速轮询读
![](https://i-blog.csdnimg.cn/blog_migrate/71bf06af28621608aaf271457b320e39.png)