epoll 中ET與LT 關於讀取處理 複習

https://zhuanlan.zhihu.com/p/21374980

===============================================linux

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的狀態session

 

關鍵是ET模型這裏寫的很好了   仔細體會 之後不會再複習了 app

Edge Triggered (ET) 邊沿觸發   
.socket的接收緩衝區狀態變化時觸發讀事件,即空的接收緩衝區  剛接收到數據時觸發讀事件
.socket的發送緩衝區狀態變化時觸發寫事件,即滿的緩衝區  剛空出空間時觸發讀事件
僅在狀態變化時觸發事件socket

 

https://blog.csdn.net/linuxheik/article/details/73294658測試

這個文章寫的也能夠可是 沒有上面的到位spa

 

由上面的狀況能夠看出 LE模式來數據時候 緩衝區不徹底讀取結束沒有事情  下次該事件他還會繼續觸發     可是ET  (默認就是非阻塞狀況哦  )則不行      必須 將緩衝區讀取結束,讀到EAGAIN | EWOULDBLOCK且 n  < 0   這樣該socket鏈接的緩衝區 下次發生事件(即有數據)時候纔會再次觸發。 .net

ET 模型 LT 模型的處理方式代碼code

#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\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;
}

  

 

和上面不相關blog

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);
}
相關文章
相關標籤/搜索