epoll函数用法,还有点poll和select

1,LT的epoll是select和poll函数的改进版。

特点是,读完缓冲区后,如果缓冲区还有内容的话,epoll_wait函数还会返回,直到把缓冲区全部读完。

2,ET的epoll(阻塞)

特点是,读完缓冲区后,不管缓冲区还有没有内容,epoll_wait函数都不会再返回,直到对端再一次发送信息过来。估计有的读者朋友会想到用while去读,但是有个致命的问题,因为文件描述符是阻塞的,所以当全部读完后,进程就会阻塞在recv函数那里,就不能够再处理别的连接了。

3,ET的epoll(非阻塞),效率最高的使用方法。

特点是,读完缓冲区后,不管缓冲区还有没有内容,epoll_wait函数都不会再返回,直到对端再一次发送信息过来。但是可以事先用fcntl把文件描述符设置成非阻塞的方式,让后用while一直去读,当全部读完后,recv函数也不会阻塞。

ET的epoll(非阻塞)的例子:

#include <stdio.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h> int main(int argc, char** argv){ int port = atoi(argv[1]);
int lfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY; bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
listen(lfd, 5); int efd = epoll_create(10); struct epoll_event re;
re.events = EPOLLIN;
re.data.fd = lfd; epoll_ctl(efd, EPOLL_CTL_ADD, lfd, &re); struct epoll_event events[100]; while(1){
int ret = epoll_wait(efd, events, 100, -1);
printf("======================wait=======\n");
if(ret == -1){
perror("epoll_wait");
exit(1);
} for(int i = 0; i < ret; ++i){
if(events[i].data.fd == lfd){
int cfd = accept(lfd, NULL, NULL); int flags = fcntl(cfd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flags); struct epoll_event re;
re.events = EPOLLIN | EPOLLET;
re.data.fd = cfd;
epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &re);
break;
}
char buf[3]; int ret;
while((ret = recv(events[i].data.fd, buf, sizeof buf, 0)) > 0){
write(STDOUT_FILENO, buf, ret);
} if(ret == 0){
epoll_ctl(efd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
close(events[i].data.fd);
printf("client disconnet\n");
}
else if(ret == -1 && errno == EAGAIN){
printf("read over\n");
}
}
}
}

poll函数例子:

#include <stdio.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h> int main(int argc, char** argv){ int port = atoi(argv[1]);
int lfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY; bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
listen(lfd, 5); struct pollfd pfd[1024];
for(int i = 0; i < 1024; ++i){
pfd[i].fd = -1;
}
pfd[0].fd = lfd;
pfd[0].events = POLLIN;
nfds_t maxfd = 0; while(1){
int ret = poll(pfd, maxfd + 1, -1);
printf("--------------poll------\n");
if(pfd[0].revents & POLLIN){
int cfd = accept(lfd, NULL, NULL);
for(int i = 0; i < 1024; ++i){
if(pfd[i].fd == -1){
pfd[i].fd = cfd;
pfd[i].events = POLLIN;
maxfd++;
break;
}
}
continue;
} for(int i = 0; i <= maxfd; ++i){
if(pfd[i].revents & POLLIN){
char buf[64];
int ret = recv(pfd[i].fd, buf, sizeof buf, 0);
if(ret == 0){
pfd[i].fd = -1;
close(pfd[i].fd);
printf("client is disconnet\n");
}
else{
write(STDOUT_FILENO, buf, ret);
}
}
} }
}

通过对比epoll和poll的例子可以看出来:

  • epoll不需要事先决定数组的大小。poll需要。
  • epoll内部是用红黑树实现的效率,不会随着连接的增多,而明显的变低。poll是用链表实现的,所以性能随着连接的增多而降低。poll还不能在windows下使用。epoll是跨平台的。
  • 顺便说下,select是用数组实现的,数组的大小由内核代码写死了,就是1024,所以想增大,只能重新编译内核。但是select是在跨平台的。

关于EPOLLOUT的补足:内核检查写的缓冲区,如果写缓冲区未满,处于可写的状态,epoll_wait函数就会返回。否则阻塞。

  • 水平模式:如果写缓冲区未满,epoll_wait会一直返回。
  • 边缘模式:epoll_wait会先返回一次;然后,写缓冲区从满的状态变成了未满的状态,epoll_wait返回。

    -注意点:调用send等函数的时候,如果写缓冲区满了的话,套接字如果是阻塞的,程序就费了,不再能相应任何事件。如果是非阻塞的话,send就会失败,有些数据就丢失了。所以,正确的做法是,当监听到EPOLLIN事件的时候,把数据读出来后,不要直接调用send等函数,要:把当前节点从树上删掉,然后加入一个EPOLLOUT的节点上去,等待epoll_wait的下一次返回,epoll_wait返回了,说明肯定可写。

select函数例子

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <unistd.h> int main(){ int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(12345);
bind(fd, (struct sockaddr*)&addr, sizeof(addr)); listen(fd, 5); fd_set readers, temp;
FD_ZERO(&readers);
FD_ZERO(&temp); FD_SET(fd, &readers); int maxfd = fd; int selret = 0; char rbuf[1024] = {0};
while(1){
temp = readers;
selret = select(maxfd + 1, &temp, NULL, NULL, NULL); if(FD_ISSET(fd, &temp)){
//server
int cfd = accept(fd, NULL, 0);
maxfd = cfd;
FD_SET(cfd, &readers);
maxfd = maxfd < cfd ? cfd : maxfd;
continue;
} //client
for(int i = fd + 1; i <= maxfd; ++i){
if(FD_ISSET(i, &temp)){ int ret = read(i, rbuf, sizeof(rbuf));
printf("recv:%s\n", rbuf);
if(ret == 0){
FD_CLR(i, &readers);
}
ret = write(i, rbuf, sizeof(rbuf));
}
}
} }

c/c++ 学习互助QQ群:877684253

本人微信:xiaoshitou5854

最新文章

  1. linux(十一)__Apache服务器
  2. C#成员函数直接调用和反射+委托的性能比较
  3. 算法:二分查找(python版)
  4. 深入理解web项目的配置文件
  5. FTP搭建
  6. RobotFramework-调用.py文件
  7. 在asp.net mvc中将checkbox传到后台时总是true的解决方法
  8. BZOJ 2763: [JLOI2011]飞行路线 spfa dp
  9. [python] 字符串引用
  10. 安卓天天练练(三)常用组件Toast
  11. Substrings
  12. 【网络流24题】最长k可重区间集(费用流)
  13. 【python标准库模块三】Os模块和Sys模块学习
  14. localhost 将您重定向的次数过多
  15. Node.js建立服务、路径处理与响应
  16. UART、SPI和I2C详解
  17. xml解析 使用dom4j操作xml
  18. PHP抽象类
  19. 【emWin】例程二十六:窗口对象——Listbox
  20. python Flask post 数据 输出

热门文章

  1. 修改postgresql 密码
  2. 原生表格table
  3. show()和隐藏hide() slideDown()和 slideUp() fadeIn()和fadeOut()
  4. c 和 c++ 啊 Make
  5. 四,专著研读(K-近邻算法)
  6. MongoTemplate 分组分页复合条件查询
  7. Fontconfig error: Cannot load config file &quot;infinality/conf.d&quot;
  8. python--小确幸
  9. asp.net mvc 系统操作日志设计
  10. VBA基础 - 函数和模块