• 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 <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);
    }
    

      

  • 相关阅读:
    一个提高N倍系统新能的编程点,却总是被普通开发们遗忘
    工作不到一年,做出了100k系统,老板给我升职加薪
    offer收割机也有方法论
    最长公共前缀
    罗马数字转整数
    回文数
    整数反转
    两数之和
    网页中Office和pdf相关文件导出
    搭建一个低配版的Mock Server
  • 原文地址:https://www.cnblogs.com/zhangkele/p/10562529.html
Copyright © 2020-2023  润新知