我是靠谱客的博主 火星上朋友,这篇文章主要介绍彻底弄懂EPOLLOUT事件EPOLLOUT事件触发的条件及典型用法Demo,现在分享给大家,希望可以做个参考。

EPOLLOUT事件触发的条件及典型用法

epoll是linux下实现IO multiplex的利器。一般编程实现方式如下:

  1. 使用epoll_create创建一个epoll fd
  2. 使用epoll_ctl往epoll fd里添加需要监听的fd,并注册需要监听的事件(由于EPOLLLET边缘触发方式更加高效,所以一般都使用边缘触发方式)
  3. 使用epoll_wait等待事件,然后依次处理各个事件,反复循环

其最常用的网络事件为EPOLLIN和EPOLLOUT,EPOLLIN对应为有socket缓冲区数据可读(当又收到了对端的一些数据,就会触发;或者作为服务端时有连接连过来),EPOLLOUT对应socket缓冲区可写。
这边EPOLLIN比较好理解,但是EPOLLOUT事件小白用户可能比较难理解了。比如经常看到小白用户的疑问:客户端需要发送数据给服务端,直接调用send(fd, ...)发送即可,看着和EPOLLOUT事件没有啥关系(说明:这种一般情况下可以,但是当缓存区满就GG了)。所以本文下面以边缘触发下的场景为例,重点说下什么时候会触发EPOLLOUT。
下面总结了三种场景下的EPOLLOUT并分别说明:

  • 客户端连接场景
    触发条件:客户端connect上服务端后,得到fd,这时候把fd添加到epoll 事件池里面后,因为连接可写,会触发EPOLLOUT事件
  • 客户端发包场景
    触发条件:缓冲区从满到不满,会触发EPOLLOUT事件
    典型应用场景(数据包发送问题):
    数据包发送逻辑:将数据包发完内核缓冲区–>进而由内核再将缓冲区的内容发送出去;这边send只是做了第一部分的工作,如果缓存区满的话send将会得到已发送的数据大小(成功放到缓冲区的),而不是整个数据包大小。
    这种情况我们可以借助EPOLLOUT事件加以解决:如果send部分成功,则表示缓存区满了,那么把剩下的部分交给epoll,当检测到EPOLLOUT事件后,再将剩余的包发送出去。
  • 重新注册EPOLLOUT事件
    触发条件:如果当连接可用后,且缓存区不满的情况下,调用epoll_ctl将fd重新注册到epoll事件池(使用EPOLL_CTL_MOD),这时也会触发EPOLLOUT时间。
    典型应用场景:
    send或write发包函数会涉及系统调用,存在一定开销,如果能将数据包聚合起来,然后调用writev将多个数据包一并发送,则可以减少系统调用次数,提高效率。这时EPOLLOUT事件就派上用场了:当发包时,可以将先数据包发到数据buffer(用户缓存区)中存放,然后通过重新注册EPOLLOUT事件,从而触发EPOLLOUT事件时,再将数据包一起通过writev发送出去。

Demo

贴下测试代码,有兴趣的同学可以基于此demo自己实践下,加深理解:

  • 客户端代码
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <stdio.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <netdb.h> #include <netinet/tcp.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <string.h> #include <sys/epoll.h> #include <stdlib.h> #define PORT 2222 // Your server port #define SERVER_IP "127.0.0.1" int main() { int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd == -1) { printf("create socket errorn"); return -1; } struct sockaddr_in addr = {0}; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); inet_aton(SERVER_IP, &addr.sin_addr); int ret = connect(fd, (struct sockaddr*)&addr, sizeof(struct sockaddr)); if(ret) { printf("connect to server failed, error = %sn", strerror(errno)); close(fd); return -1; } int efd = epoll_create1(0); struct epoll_event event; event.data.fd = fd; event.events = EPOLLOUT | EPOLLET; int s = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event); if (s == -1) { perror ("epoll_ctl error"); close(fd); close(efd); abort(); } #define MAXEVENTS 10 struct epoll_event *events; events = (struct epoll_event *) calloc (MAXEVENTS, sizeof event); while (1) { int n = epoll_wait(efd, events, MAXEVENTS, 1000); usleep(100000); for (int i = 0; i < n; i++) { if (events[i].events & EPOLLOUT) { printf("received epollout eventn"); printf("Start send hellon"); char input[] = "hello"; send(fd, input, sizeof(input), 0); // send packet // mod epollout event to trigger event struct epoll_event event; event.data.fd = fd; event.events = EPOLLOUT | EPOLLET; epoll_ctl(efd, EPOLL_CTL_MOD, fd, &event); } } } close(fd); close(efd); return 0; }
  • 服务端代码
    见弄懂Linux epoll 中的服务端代码实现

最后

以上就是火星上朋友最近收集整理的关于彻底弄懂EPOLLOUT事件EPOLLOUT事件触发的条件及典型用法Demo的全部内容,更多相关彻底弄懂EPOLLOUT事件EPOLLOUT事件触发内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(88)

评论列表共有 0 条评论

立即
投稿
返回
顶部