epoll 中ET与LT 关于读取处理 复习

epoll 中ET与LT 关于读取处理 复习

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

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#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\n" );

memset( buf, '\0', BUFFER_SIZE );

int ret = recv( sockfd, buf, BUFFER_SIZE-1, 0 );

if( ret <= 0 )

{

close( sockfd );

continue;

}

printf( "get %d bytes of content: %s\n", ret, buf );

}

else

{

printf( "something else happened \n" );

}

}

}

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\n" );

while( 1 )

{

memset( buf, '\0', 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\n" );

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\n", ret, buf );

}

}

}

else

{

printf( "something else happened \n" );

}

}

}

int main( int argc, char* argv[] )

{

if( argc <= 2 )

{

printf( "usage: %s ip_address port_number\n", 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\n" );

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", n);

string& readed = cons[fd].readed;

readed.append(buf, n);

if (readed.length()>4) {

if (readed.substr(readed.length()-2, 2) == "\n\n" || readed.substr(readed.length()-4, 4) == "\r\n\r\n") {

//当读取到一个完整的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\n", fd, errno, strerror(errno));

}

close(fd);

cons.erase(fd);

}

相关推荐

用五大核心财务指标精准选股:策略与实践
如何打开mobile365

用五大核心财务指标精准选股:策略与实践

📅 01-28 👁️ 7040
《情深深雨蒙蒙》那些难忘上海景,揭秘拍摄地探秘
如何打开mobile365

《情深深雨蒙蒙》那些难忘上海景,揭秘拍摄地探秘

📅 01-08 👁️ 6135
收钱POS机哪个好用?2024年最全测评,这5款让你收款快人一步!
尼日利亚
如何打开mobile365

尼日利亚

📅 08-26 👁️ 3773
美金账户开设后的做账指南:让您的财务管理更高效
365娱乐官网官方网站

美金账户开设后的做账指南:让您的财务管理更高效

📅 11-27 👁️ 2060
《王者荣耀》梦境修炼为什么有的英雄没有
如何打开mobile365

《王者荣耀》梦境修炼为什么有的英雄没有

📅 08-31 👁️ 8694