epoll的ET和LT模式下,accept,recv,send写法
来源:互联网 发布:网络双向是什么意思 编辑:程序博客网 时间:2024/06/05 09:03
epoll有两种事件模型:
Level Triggered (LT) 水平触发
socket接收缓冲区不为空,有数据可读,读事件一直触发
socket发送缓冲区不满,可以继续写入数据,写事件一直触发
Edge Triggered (ET) 边缘触发
socket的接收缓冲区状态变化时触发读事件,即空的接收缓冲区刚接收到数据时触发读事件
socket的发送缓冲区状态变化时触发写事件,即满的缓冲区刚空出空间时触发读事件
总结:
水平触发:只要可读,就一直触发读事件,只要可写,就一直触发写事件
边缘触发:从不可读变为可读,从不可写变为可写,只触发一次
举个例子理解一下边缘触发,比如写事件,我们开始连接成功之后,socket从不可写变为可写,触发一次;发送缓冲已经满了,然后变成了不满,那么又可以触发一次。(强调状态的变化)
1. accept的写法
while (true){ struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); evutil_socket_t fd = accept(fd, (struct sockaddr *)&addr, &addr_len); if (fd == -1) { break; } log(LOG_DEBUG, "New accept ip:%s socket:%d", inet_ntoa(addr.sin_addr), fd); evutil_make_socket_nonblocking(fd); int optval = 1; setsockopt(fd, SOL_SOCKET, TCP_NODELAY, &optval, sizeof(optval));}
为什么要用while?当epoll的读事件触发之后,我们要判断这个套接字是不是监听套接字,因为accept也是一个读事件。好了,当这里是一个accept事件时,我们最好用while包起来。因为在大并发的情况下,同时向服务器发起多个连接是很正常的,那么如果触发一次accept事件,你只接收一个连接,那么需要多次accept事件轮询触发你才可以连接完所有,你可以理解为让后面一些连接有”延误“。如果使用while,我们触发了accept事件就直接一直不停的accept直到accept返回-1,也就是没有新连接来了,这样效率更高。
2. recv的写法
struct evbuffer* input = evbuffer_new();//数据缓冲while (true){ char buffer[1024] = { '\0' }; int ret = recv(fd, buffer, 1024, 0); //从接收缓冲取数据成功 if (ret > 0) { evbuffer_add(input, buffer, ret); //不足1024,说明取完了 if (ret < 1024) { break; } } //发生错误 if (ret == -1) { //EAGAIN/EWOULDBLOCK提示你的应用程序现在没有数据可读请稍后再试 //EINTR指操作被中断唤醒,需要重新读 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { break; } else if (errno == EINTR) { continue; } //异常断开情况 else { close(fd); break; } } //接收到主动关闭请求 if (ret == 0) { close(fd); break; }}
由于tcp是流,我们也不知道接收缓冲有多少个包,长度多少,所以我们可以用一个固定长度1024,每次取1k数据,直到把数据取完。边缘触发只能使用这种写法!
当使用水平触发模式的时候,我们读事件触发,可以取8k数据就走,不要在这一个读事件这里太久,不耽误后面事件的触发,反正读事件会后面一直触发,下次再取8k继续就好。但是如果是边缘模式,那么读事件触发,你一定要一次把数据全部取完,因为它只触发一次,这就是区别。
3. send的写法
string response;//发送的数据unsigned int pos = 0;do { int ret = send(fd, response.data.c_str()+pos, response.data.length()-pos, 0); //发生错误 if (-1 == ret) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)) { ret = 0; continue; } //异常断开 else { close(fd); break; } } //接收到主动关闭请求 if (0 == ret) { close(fd); break; } pos += ret;} while (pos < response.data.length());
关于写事件,我们很少情况下会使用,一般的网络编程模型,都是主线程一个事件循环用来监听连接,接收连接之后,会把连接对象丢给IO线程池,这个IO线程池一般线程数量等于cpu核数,这个IO线程池每个人都有自己的epoll,来监听连接对象的可读事件,(多线程使用epoll的安全原因我的另外一篇博客有写,从epoll源码分析它的使用)IO线程读完数据,会进行tcp的粘包,半包处理,然后封装成task,最后丢给一个工作线程池,工作线程池处理task,最后处理完task需要回复,就给一个专门的发送线程,这个发送线程统一发送数据,所以写事件用的少。优化方案的发送是这样的,先用一个单独的发送线程发送数据,如果发现数据失败,才会把这个socket添加到epoll关心一下写事件,等可写再发,这样优化发送线程的发送问题。
- epoll的ET和LT模式下,accept,recv,send写法
- epoll在LT和ET模式下的读写方式
- epoll在LT和ET模式下的读写方式
- epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- epoll在LT和ET模式下的读写方式
- epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- Epoll在LT和ET模式下的读写方式
- PX4代码学习系列博客(6)——offboard模式位置控制代码分析
- tensorflow variable_scope,tf.name_scope, tf.variable, tf.get_varible
- #leetcode编程日记#343. Integer Break
- java 图片文件生成MD5值
- 在centos中视频转音频的命令
- epoll的ET和LT模式下,accept,recv,send写法
- 以半桥驱动芯片FAN73933为例说明自举原理
- 使用Python Selenium PhantomJS的XPath时应注意校验结果
- 关于Android调用支付宝接口”有的手机无法调起网页支付”遇到的问题
- size_t的使用实例 @C++
- android:windowSoftInputMode属性详解
- Linux基础教程
- DNS介绍
- Linux下编译并运行的第一个程序