zoukankan      html  css  js  c++  java
  • epoll的lt和et模式的实验

    针对epoll api的两种触发模式,lt和et,仿照一些例子写了代码进行实验。

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/epoll.h>
    #include <pthread.h>
    
    #include <assert.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    #include <errno.h>
    #include <string.h>
    #include <fcntl.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) {
                            sockaddr_in client_address;
                            socklen_t client_addrlen = sizeof(client_address);
                            int connfd = accept(listenfd, (sockaddr*)&client_address,
                                                    &client_addrlen);
                            addfd(epollfd, connfd, false);
                    }
                    else if (events[i].events & EPOLLIN) {
                            printf("lt 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) {
                            sockaddr_in client_address;
                            socklen_t client_addrlen = sizeof(client_address);
                            int connfd = accept(listenfd, (sockaddr*)&client_address,
                                                    &client_addrlen);
                            addfd(epollfd, connfd, true);
                    }
                    else if(events[i].events & EPOLLIN) {
                            // Need to read complete
                            printf("et event trigger once
    ");
                            while (true) {
                                    memset(buf, '', BUFFER_SIZE);
                                    int ret = recv(sockfd, buf, BUFFER_SIZE-1, 0);
                                    if (ret < 0) {
                                            // Below shows complete
                                            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                                                    printf("read later
    ");
                                                    break;
                                            }
                                            printf("some error happens
    ");
                                            close(sockfd);
                                            break;
                                    }
                                    else if (ret == 0) {
                                            close(sockfd);
                                            break;
                                    }
                                    else {
                                            printf("get %d bytes of content: %s
    ", ret, buf);
                                    }
                            }
                    }
                    else {
                            printf("something else happened
    ");
                    }
            }
    }
    
    int main(int argc, char *argv[]) {
            if (argc <= 1) {
                    printf("usage: %s port_number ip_address
    ", basename(argv[0]));
                    return 1;
            }
    
            int port = atoi(argv[1]);
            int ret = 0;
            sockaddr_in address;
            bzero(&address, sizeof(address));
            address.sin_family = AF_INET;
            if (argc >= 3) {
                    const char *ip =argv[2];
                    inet_pton(AF_INET, ip, &address.sin_addr);
            }
            else {
                    address.sin_addr.s_addr = INADDR_ANY;
            }
            address.sin_port = htons(port);
    
            int listenfd = socket(PF_INET, SOCK_STREAM, 0);
            assert(listenfd >= 0);
    
            ret = bind(listenfd, (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(true) {
                    ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
                    if (ret < 0) {
                            printf("epoll failure
    ");
                            break;
                    }
                    lt(events, ret, epollfd, listenfd); // lt
                    //et(events, ret, epollfd, listenfd); // et
            }
    
            close(listenfd);
            return 0;
    
    }

    Makefile文件:

    epoll_test : epoll_test.cpp
            g++ -o epoll_test epoll_test.cpp -lpthread

    以上程序有个问题,就是在端口被占用时候,因为bind失败,会assert失败然后core dump. 在重复测试时候,可以换个端口。

    首先,注释掉et,使用lt:

                    lt(events, ret, epollfd, listenfd); // lt
                    //et(events, ret, epollfd, listenfd); // et

    运行 ./epoll_test 12888 并在另一个窗口用telnet输入超过10个(BUFFERSIZE)字符:

    $telnet 127.0.0.1 12888
    Trying 127.0.0.1...
    Connected to 127.0.0.1.
    Escape character is '^]'.
    12345678901234567890123456
    1234567890123456789012345678

    在服务器端得到:

    $./epoll_test 12888
    lt event trigger once
    get 9 bytes of content: 123456789
    lt event trigger once
    get 9 bytes of content: 012345678
    lt event trigger once
    get 9 bytes of content: 90123456
    lt event trigger once
    get 1 bytes of content: 
    
    lt event trigger once
    get 9 bytes of content: 123456789
    lt event trigger once
    get 9 bytes of content: 012345678
    lt event trigger once
    get 9 bytes of content: 901234567
    lt event trigger once
    get 3 bytes of content: 8

    可以看出因为buffersize的限制,服务器端进行了多次读取,event也触发了多次。

    换成et模式:

                    //lt(events, ret, epollfd, listenfd); // lt
                    et(events, ret, epollfd, listenfd); // et

    运行服务器后,telnet客户端输入:

    $telnet 127.0.0.1 12889
    Trying 127.0.0.1...
    Connected to 127.0.0.1.
    Escape character is '^]'.
    12345678901234567890123456
    1234567890123456789012345678

    服务器显示:

    $./epoll_test 12889
    et event trigger once
    get 9 bytes of content: 123456789
    get 9 bytes of content: 012345678
    get 9 bytes of content: 90123456
    get 1 bytes of content: 
    
    read later
    et event trigger once
    get 9 bytes of content: 123456789
    get 9 bytes of content: 012345678
    get 9 bytes of content: 901234567
    get 3 bytes of content: 8
    
    read later

    可以看出,每次客户端的字符串,只触发了一次。

    其实,上面的例子还不够严谨,因为服务器一次已经把字符都读完了。那么如果没读完,会继续出发吗。

    如下修改服务器代码:

                            //修改
                            //while (true) {
                            if (true) {
                                    memset(buf, '', BUFFER_SIZE);
                                    int ret = recv(sockfd, buf, BUFFER_SIZE-1, 0);
                                    if (ret < 0) {
                                            // Below shows complete
                                            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                                                    printf("read later
    ");
                                                    break;
                                            }
                                            printf("some error happens
    ");
                                            close(sockfd);
                                            //修改
                                            //break;
                                    }
                                    else if (ret == 0) {
                                            close(sockfd);
                                            //修改
                                            //break;
                                    }
                                    else {
                                            printf("get %d bytes of content: %s
    ", ret, buf);
                                    }
                            }        

    运行服务器之后,telnet输入长字符串:

    $telnet 127.0.0.1 12888
    Trying 127.0.0.1...
    Connected to 127.0.0.1.
    Escape character is '^]'.
    12345678901234567890123456

    服务器端只显示了BUFFERSIZE长度的一行,没有读入的数据也没有进行event触发:

    $./epoll_test 12888
    et event trigger once
    get 9 bytes of content: 123456789

    如果客户端,再输入一行:

    $telnet 127.0.0.1 12888
    Trying 127.0.0.1...
    Connected to 127.0.0.1.
    Escape character is '^]'.
    12345678901234567890123456
    1234567890123456789012345678

    服务器端也仅仅把之前没读入的上一次客户端发来的数据中,再读入BUFFERSIZE长度:

    $./epoll_test 12888
    et event trigger once
    get 9 bytes of content: 123456789
    et event trigger once
    get 9 bytes of content: 012345678

    另外,对上面的服务器端程序,增加了et模式下对recv函数的返回ret=0的打印:

                                    else if (ret == 0) {
                                            printf("get 0 data
    ");
                                            close(sockfd);
                                            break;
                                    }

    发现在et模式下,没有走到ret==0的分支:

    $telnet 127.0.0.1 12889
    Trying 127.0.0.1...
    Connected to 127.0.0.1.
    Escape character is '^]'.
    12345678901234567890123456

    服务器端:

    $./epoll_test 12889
    et event trigger once
    get 9 bytes of content: 123456789
    get 9 bytes of content: 012345678
    get 9 bytes of content: 90123456
    get 1 bytes of content: 
    
    read later

    走的是如下的判断结束的分支:

                                            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                                                    printf("read later
    ");
                                                    break;
                                            }

    以上。:)

  • 相关阅读:
    我对什么是真正的对象,以及软件中的对象在分析阶段、设计阶段、实现阶段的一些看法
    通过分析蜘蛛侠论坛中的版块管理功能来介绍该如何使用我开发出来的ROM框架
    蜘蛛侠论坛核心框架分析1 如何设计与实现当前访问用户
    关于DDD领域驱动设计的理论知识收集汇总
    分享一个简易的ORM框架源代码以及基于该框架开发的一个简易论坛源代码
    微软的一个开源项目Oxite学习后的感受
    AgileEAS.NET平台开发实例药店系统UI层分析
    AgileEAS.NET平台开发实例药店系统BLL层分析
    AgileEAS.NET平台开发实例药店系统DAL层解析
    AgileEAS.NET平台开发实例药店系统系统架构设计
  • 原文地址:https://www.cnblogs.com/charlesblc/p/5521086.html
Copyright © 2011-2022 走看看