struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
一、函数解析
#include
int epoll_create(int size)
1. size不是最大值,而是内核如何对内部结构进行维度设置的提示。
2. epoll_create返回的文件描述符必须使用close关闭。
返回值:成功(非负文件描述符) 失败(-1)
errno(EINVAL-size非正 | ENFILE-文件描述符用完 | ENOMEM-内存不足)
#include
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
events成员有:
EPOLLIN 关联的文件描述符可读
EPOLLOUT 关联的文件描述符可写
EPOLLRDHUP 流式套接字对端关闭连接或关闭写通道(ET模式写非常有用)2.6.17
EPOLLPRI 关联的文件描述符紧急数据可读
EPOLLERR 关联的文件描述符发生错误
EPOLLHUP 关联的文件描述符挂起
EPOLLET ET模式
EPOLLONESHOT 关联的文件描述符设置一次性行为 2.6.2
op操作有:
EPOLL_CTL_ADD 增加
EPOLL_CTL_MOD 修改
EPOLL_CTL_DEL 删除
返回值:成功(0) 失败(-1)
errno(EBADF-epfd或者fd不是合法的 | EEXIST-重复增加 | EINVAL-epfd不是epoll描述符或者epfd=fd | ENOENT-修改删除的fd不在epoll中 | ENOMEM-内存不足 | EPERM-fd不支持epoll)
#include
int epoll_wait (int epfd, struct epoll_event *events, int maxevents, int timeout);
int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);
1. timeout为0表示立马返回, 为-1表示无限等待
2. 超时或者达到maxevents都会返回
返回值:成功(就绪的文件描述符数量) 失败(-1)
errno(EBADF-epfd不合法 | EFAULT-events没有写权限 | EINTR-超时 | EINVAL-epfd不是epoll描述符或者maxevents小于0)
二、写过程
水平触发(LT):只要写缓冲区还有空间,就返回写就绪。
边缘触发(ET):
1.首次加入epoll且写缓冲区有空间,返回写就绪(参考四用例第一次调用printf)
2.写缓冲区内容被取走,返回写就绪(参考四用例的fflush,\n有类似作用)
3.EPOLL_CTL_MOD修改关联文件描述符event,且写缓冲区有空间,返回写就绪(参考四用例epoll_ctl)
三、读过程
水平触发:只要读缓冲区有数据,就返回可读。
边缘触发:
数据到来的时候返回可读。(即如果上一次没有读完的数据,需要等到下一次数据到来的时候才能继续读)。
四、用例(写过程)
#include
#include
#define STDOUT_FILENO 1
int main(void) {
int epfd, nfds;
struct epoll_event ev, events[5]; //ev用于注册事件,数组用于返回要处理的事件
epfd = epoll_create(1); //只需要监听一个描述符——标准输出
ev.data.fd = STDOUT_FILENO;
ev.events = EPOLLOUT | EPOLLET; //监听读状态同时设置ET模式
epoll_ctl(epfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev); //注册epoll事件
for (;;) {
nfds = epoll_wait(epfd, events, 5, -1);
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == STDOUT_FILENO) {
printf( "hello world!");
// ev.data.fd=STDOUT_FILENO;
// ev.events=EPOLLOUT|EPOLLET;
// epoll_ctl(epfd,EPOLL_CTL_MOD,STDOUT_FILENO,&ev); //重新MOD事件(ADD无效),返回写就绪,循环输出
// fflush(stdout); //读取写缓冲区数据,返回写就绪,循环输出
}}}}
五、如何判断客户端关闭连接
1. TCP recv返回0, 说明对方关闭
2. 注册EPOLLERR, 收到事件是关闭
3. recv/send 返回-1时, 如果错误不是EWOULDBLOCK或者EINTR, 也主动关闭连接。