博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
epoll下 LT与ET的区别
阅读量:3940 次
发布时间:2019-05-24

本文共 4622 字,大约阅读时间需要 15 分钟。

文章目录

lt 与 et 是epoll操作文件描述符的两种方式,lt 是 效率比较高的poll,et 是 epoll 的高效模式。

lt 当检测到epoll_wait 上有就绪事件的时候,可以不立即执行,当下次再次调用epoll_wait时还会提醒
et 当检测到epoll_wait 上有就绪的事件的时候,就必须处理该事件,否则下次调用epoll_wait 的时候,也不会提醒。

例如: 客户端向服务器发送数据,

lt 模式下,如果服务端一次没有将全部的数据读完,则epoll_wait 会再次提醒,直到把所有的数据全部读完为止
et 模式下,如果未将全部的数据读完,则epoll_wait 不再提醒,剩余的数据还在接收方的接收缓冲区中。

netstat -napt 可以查看接收缓冲区的内容

再lt 模式下的代码实现

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX 10//创建套接字int create_sockfd(){ int sockfd = socket(AF_INET, SOCK_STREAM, 0); assert( sockfd != -1 ); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(6000); addr.sin_addr.s_addr = inet_addr("127.0.0.1"); int res = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); assert(res != -1); listen(sockfd, 5); return sockfd;}//添加void epoll_add(int epfd,int fd){ struct epoll_event ev; ev.data.fd = fd; //未开启et模式 ev.events = EPOLLIN; if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) { perror("epoll_ctl add error"); }}//移除void epoll_del(int epfd, int fd){ if(epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL) == -1) { perror("epoll_ctl del error"); }}int main(){ int sockfd = create_sockfd(); int epfd = epoll_create(MAX); assert(epfd != -1); struct epoll_event ev[MAX]; epoll_add(epfd, sockfd); while(1) { int n = epoll_wait(epfd, ev, MAX, 5000); if(n == -1) { printf("pollfd error\n"); continue; } else if(n == 0) { printf("time out\n"); } else { int i = 0; for(; i < n ; i++) { int fd = ev[i].data.fd; if(ev[i].events & EPOLLIN) { if(fd == sockfd) { struct sockaddr_in caddr; int len = sizeof(caddr); int c = accept(fd, (struct sockaddr*) &caddr, &len); if(c == -1) { continue; } printf("new connect\n"); epoll_add(epfd, c); } else { //recv char buff[128] = { 0}; int n = recv(fd, buff, 127, 0); if(n <= 0) { epoll_del(epfd, fd); close(fd); } else { printf("%s\n",buff); send(fd, "ok", 2, 0); } } } } } } return 0;}

et 模式下

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX 10//设置为非阻塞方式/*在非阻塞方式下如果没有数据读会返回-1, 这样就可以根据读函数的返回值判断数据是否全部读完。当接收到可读的事件之后,循环进行读取,直到把数据全部读完为止。*/void setNonBlock(int fd){ int oldfl = fcntl(fd, F_GETFL); int newfl = oldfl | O_NONBLOCK; if(fcntl(fd, F_SETFL, newfl) == -1) { perror("setNonBlock error"); }}//创建套接字int create_sockfd(){ int sockfd = socket(AF_INET, SOCK_STREAM, 0); assert( sockfd != -1 ); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(6000); addr.sin_addr.s_addr = inet_addr("127.0.0.1"); int res = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); assert(res != -1); listen(sockfd, 5); return sockfd;}//添加void epoll_add(int epfd,int fd){ struct epoll_event ev; ev.data.fd = fd; //在这里开启et模式 ev.events = EPOLLIN | EPOLLET; //设置文件描述符为非阻塞模式 setNonBlock(fd); if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) { perror("epoll_ctl add error"); }}//移除void epoll_del(int epfd, int fd){ if(epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL) == -1) { perror("epoll_ctl del error"); }}int main(){ int sockfd = create_sockfd(); int epfd = epoll_create(MAX); assert(epfd != -1); struct epoll_event ev[MAX]; epoll_add(epfd, sockfd); while(1) { int n = epoll_wait(epfd, ev, MAX, 5000); if(n == -1) { printf("pollfd error\n"); continue; } else if(n == 0) { printf("time out\n"); } else { int i = 0; for(; i < n ; i++) { int fd = ev[i].data.fd; if(ev[i].events & EPOLLIN) { if(fd == sockfd) { //循环处理connect 发来的连接事件 while(1) { struct sockaddr_in caddr; int len = sizeof(caddr); int c = accept(fd, (struct sockaddr*) &caddr, &len); if(c == -1) { break; } printf("new connect\n"); epoll_add(epfd, c); } } else { //循环处理接收数据的事件 while(1) { char buff[128] = { 0}; int n = recv(fd, buff, 1, 0); if(n == 0) { epoll_del(epfd, fd); close(fd); break; } else if(n == -1) { send(fd, "ok", 2, 0); break; } else { printf("%s\n",buff); } } } } } } } return 0;}

et 模式下一定要采取非阻塞模式,除非特殊的要求,下面看一下阻塞模式下recv 函数的问题

char buff[128] = {
0}; //这里每次只接收一个数据 int n = recv(fd, buff, 1, 0); if(n <= 0) {
epoll_del(epfd, fd); close(fd); } else {
printf("%s\n",buff); send(fd, "ok", 2, 0); }

1、当每次发来的数据较多时,一次无法全部接收,则只有等到下一个epoll_wait 返回时才开始接收剩余的数据。如果没有epoll_wait 事件发生,则无法接收剩余的数据。

2、当缓冲区的还有数据的时候,客户端关闭了,这时候epoll_wait 函数也会检测到有就绪事件发生,在上面的代码中,此时recv 接收的数据的个数不是0, 而是1。这时服务器会回复一个ok, 但是服务端已经关闭,所以程序会产生错误。
如果没有send 函数,服务端是可以将数据全部接收的,因为客户端关闭了套接字,此时服务端并没有清除该文件描述符,所以每次epoll_wait 检测都会检测到有事件发生,直到把数据全部读出然后清除文件描述符。

转载地址:http://oxnwi.baihongyu.com/

你可能感兴趣的文章
配置CakePHP
查看>>
JQuery中$.ajax()方法参数详…
查看>>
JS&nbsp;简易滚动条
查看>>
PHP&nbsp;__call()方法
查看>>
JS中的call()和apply()方法
查看>>
慎用PHP$_REQUEST数组
查看>>
详细解释PHP中header
查看>>
php中的迭代器Iterator的具体用法
查看>>
mysql操作技巧随笔--链表删除数据
查看>>
MySql在建立索引优化时需要…
查看>>
Mysql建表和索引使用规范
查看>>
mysql&nbsp;队列&nbsp;实现并发读
查看>>
MYSQL千万级数据量的优化方法积累
查看>>
经典分享MySQL的limit查询优化
查看>>
各大浏览器兼容性报告
查看>>
统计每个ip的访问量--linux--acces…
查看>>
常见hash算法的原理
查看>>
localForage——轻松实现&nbsp;Web&amp;n…
查看>>
yaf使用小记
查看>>
document.domain&nbsp;跨域问题
查看>>