https://zhuanlan.zhihu.com/p/21374980
===============================================
https://zhuanlan.zhihu.com/p/21619218?utm_medium=social&utm_source=wechat_session&from=timeline&isappinstalled=0&s_s_i=akD%2BEQGuE2ymUvWOlk%2BEkZpTXXDTr7pm24gvEBEAW%2Fg%3D&s_r=1
EPOLL事件的两种模型:
Level Triggered (LT) 水平触发
.socket接收缓冲区不为空 有数据可读 读事件一直触发
.socket发送缓冲区不满 可以继续写入数据 写事件一直触发
符合思维习惯,epoll_wait返回的事件就是socket的状态
关键是ET模型这里写的很好了 仔细体会 以后不会再复习了
Edge Triggered (ET) 边沿触发
.socket的接收缓冲区状态变化时触发读事件,即空的接收缓冲区 刚接收到数据时触发读事件
.socket的发送缓冲区状态变化时触发写事件,即满的缓冲区 刚空出空间时触发读事件
仅在状态变化时触发事件
https://blog.csdn.net/linuxheik/article/details/73294658
这个文章写的也可以但是 没有上面的到位
由上面的情况可以看出 LE模式来数据时候 缓冲区不完全读取结束没有事情 下次该事件他还会继续触发 但是ET (默认就是非阻塞情况哦 )则不行 必须 将缓冲区读取结束,读到EAGAIN | EWOULDBLOCK且 n < 0 这样该socket连接的缓冲区 下次发生事件(即有数据)时候才会再次触发。
ET 模型 LT 模型的处理方式代码
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <fcntl.h> #include <stdlib.h> #include <sys/epoll.h> #include <pthread.h> #define MAX_EVENT_NUMBER 1024 #define BUFFER_SIZE 10 int setnonblocking( int fd ) { int old_option = fcntl( fd, F_GETFL ); int new_option = old_option | O_NONBLOCK; fcntl( fd, F_SETFL, new_option ); return old_option; } void addfd( int epollfd, int fd, bool enable_et ) { epoll_event event; event.data.fd = fd; event.events = EPOLLIN; if( enable_et ) { event.events |= EPOLLET; } epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event ); setnonblocking( fd ); } void lt( epoll_event* events, int number, int epollfd, int listenfd ) { char buf[ BUFFER_SIZE ]; for ( int i = 0; i < number; i++ ) { int sockfd = events[i].data.fd; if ( sockfd == listenfd ) { struct sockaddr_in client_address; socklen_t client_addrlength = sizeof( client_address ); int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength ); addfd( epollfd, connfd, false ); } else if ( events[i].events & EPOLLIN ) {//只要socket读缓存区中还有未读出的数据 这段代码就会不停的被触发 即使关闭了sock printf( "event trigger once " ); memset( buf, ' ', BUFFER_SIZE ); int ret = recv( sockfd, buf, BUFFER_SIZE-1, 0 ); if( ret <= 0 ) { close( sockfd ); continue; } printf( "get %d bytes of content: %s ", ret, buf ); } else { printf( "something else happened " ); } } } void et( epoll_event* events, int number, int epollfd, int listenfd ) { char buf[ BUFFER_SIZE ]; for ( int i = 0; i < number; i++ ) { int sockfd = events[i].data.fd; if ( sockfd == listenfd ) { struct sockaddr_in client_address; socklen_t client_addrlength = sizeof( client_address ); int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength ); addfd( epollfd, connfd, true ); } else if ( events[i].events & EPOLLIN ) { //for 循环到某个sock连接的缓存区有数据 可读那么就要 while读取读到缓存区没有数据 ET模式 //确保把socket缓冲区存储的数据读取结束 才break 退出while printf( "event trigger once " ); while( 1 ) { memset( buf, ' ', BUFFER_SIZE ); int ret = recv( sockfd, buf, BUFFER_SIZE-1, 0 ); if( ret < 0 ) {//非阻塞ET模式 下面成立表示sock缓冲区数据读取结束了 此后epoll_wait就能再次触发,就能再一次触发fd 上的EPOLLIN事件 if( ( errno == EAGAIN ) || ( errno == EWOULDBLOCK ) ) { //无需关闭socket 只说明现在缓冲区没有数据可以读取了 等待下一次触发处理 printf( "read later " ); break;//这里 缓冲区数据读取over了 所以break掉当前读取sock的while循环 } //返回-1 那么说明发生其他错误不是上面的两种错误直接关闭sock close( sockfd ); break; } else if( ret == 0 ) { close( sockfd );// ==0 ET模式返回0 表示对端已经关闭了 } else { //接收ok printf( "get %d bytes of content: %s ", ret, buf ); } } } else { printf( "something else happened " ); } } } int main( int argc, char* argv[] ) { if( argc <= 2 ) { printf( "usage: %s ip_address port_number ", basename( argv[0] ) ); return 1; } const char* ip = argv[1]; int port = atoi( argv[2] ); int ret = 0; struct sockaddr_in address; bzero( &address, sizeof( address ) ); address.sin_family = AF_INET; inet_pton( AF_INET, ip, &address.sin_addr ); address.sin_port = htons( port ); int listenfd = socket( PF_INET, SOCK_STREAM, 0 ); assert( listenfd >= 0 ); ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) ); assert( ret != -1 ); ret = listen( listenfd, 5 ); assert( ret != -1 ); epoll_event events[ MAX_EVENT_NUMBER ]; int epollfd = epoll_create( 5 ); assert( epollfd != -1 ); addfd( epollfd, listenfd, true ); while( 1 ) { int ret = epoll_wait( epollfd, events, MAX_EVENT_NUMBER, -1 ); if ( ret < 0 ) { printf( "epoll failure " ); break; } //lt( events, ret, epollfd, listenfd ); et( events, ret, epollfd, listenfd ); } close( listenfd ); return 0; }
和上面不相关
void handleRead(int efd, int fd) { char buf[4096]; int n = 0; while ((n=::read(fd, buf, sizeof buf)) > 0) { if(output_log) printf("read %d bytes ", n); string& readed = cons[fd].readed; readed.append(buf, n); if (readed.length()>4) { if (readed.substr(readed.length()-2, 2) == " " || readed.substr(readed.length()-4, 4) == " ") { //当读取到一个完整的http请求,测试发送响应 sendRes(fd); } } } //这里当socket缓冲区的数据被读取完毕了,return掉 但不能close fd 因为下次该sock缓冲区有数据还会再次触发 //ET 非阻塞就是要读到 EAGAIN 且 n < 0 就是将来sock缓冲区读彻底下次才触发 if (n<0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return; //实际应用中,n<0应当检查各类错误,如EINTR if (n < 0) { printf("read %d error: %d %s ", fd, errno, strerror(errno)); } close(fd); cons.erase(fd); }