本文共 4622 字,大约阅读时间需要 15 分钟。
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/