zoukankan      html  css  js  c++  java
  • epoll模型实例

    一.epoll概述

    epoll是linux下的一个系统调用,用来监听大量文件描述符并对其上的触发事件进行处理。它是select/poll的增强版本,也是linux下多路复用io最常用的接口。要理解epoll是什么,首先得清楚什么是多路复用io。用户进行io操作需要经过内核,而如果所请求的io目前不满足条件(如需要从标准输入读取数据,而用户还未输入),这个时候内核就会让应用程序陷入等待,即阻塞状态。个人理解,io复用技术就是通过特定接口,将这种阻塞进行了转移,转嫁到了如select/poll/epoll之类多系统调用上,并且支持多个文件描述符多监测,即多路复用。这样epoll便可以替应用程序同时监听多个文件描述符,一旦有触发发生,内核就会通知应用程序进行处理。

    二.epoll接口

    epoll的函数接口既可以通过查看epoll.h文件来查看,也可以用man命令查看。这里引用epoll.h中的描述.(epoll.h在/usr/include/x86_64-linux-gnu/sys目录下可找到)。

    epoll接口主要包括一个结构三个函数

    struct epoll_event
     {
     uint32_t events;   /* Epoll事件 */
     epoll_data_t data; /* 用户变量 */
     } __EPOLL_PACKED;
    
    
     /* 创建一个epoll对象,返回其描述符.
        "size" 参数用来指定和对象相关的描述符个数.
        返回的文件描述符应该通过close()关闭   */
      int epoll_create (int __size) __THROW;
    
    
     /* 操作epoll对象"epfd". 成功返回0,失败返回-1. "op" 参数为声明多宏
        "fd"参数即操作多目标.
        "event"参数为调用者感兴趣的描述符(通过用户变量data关联)  */
      int epoll_ctl (int __epfd, int __op, int __fd,
                           struct epoll_event *__event) __THROW;
    
    
     /* 监听epoll对象"epfd". 返回触发的文件描述符到"events". 或者出错返回-1.
        "events"是用来存触发描述符的缓冲."maxevents"是返回的对大数."timeout"
      表示等待多毫秒数(-1 == 无限).
      */
      int epoll_wait (int __epfd, struct epoll_event *__events,
                            int __maxevents, int __timeout);
    

      

    三.epoll模型实例

    以下代码用于实现一个简单的服务器程序,接受客户端数据,并在服务端标准输出

    #include<fcntl.h>
    #include<iostream>
    #include<sys/epoll.h>
    #include<netinet/in.h>
    #include<strings.h>
    #include<set>
    #include<stdlib.h>
     
    #define LISTENQ 1024
    #define MAX_EVENT 20
    #define BUF_SIZE 1024
     
    using namespace std;
     
    class Server
    {
            public:
                    Server(unsigned int port = 5660);
                    ~Server();
                    void set_non_blocking(int);
                    void run();
            private:
                    unsigned int server_port;
                    struct epoll_event ev, events[MAX_EVENT];
                    int listen_fd;
                    int epoll_fd;
                    std::set<int> connections;
    };
     
    Server::Server(unsigned int port):server_port(port)
    {
            listen_fd = socket(AF_INET, SOCK_STREAM, 0);
     
            //initiate the socket address structure
            struct sockaddr_in server_addr;
            bzero(&server_addr, sizeof(server_addr));
            server_addr.sin_family         = AF_INET;
            server_addr.sin_addr.s_addr    = htonl(INADDR_ANY);
            server_addr.sin_port           = htons(server_port);
     
            //bind the local protocol type to a socket address
            bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
     
            //initiate the epoll
            epoll_fd = epoll_create(MAX_EVENT);
            set_non_blocking(listen_fd);
            ev.data.fd = listen_fd;
            ev.events = EPOLLIN | EPOLLET;
            epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev);
     
            //start listening
            listen(listen_fd, LISTENQ);
    }
     
    Server::~Server()
    {
     
    }
     
    void Server::set_non_blocking(int fd)
    {
            int flag;
            flag = fcntl(fd, F_GETFL);
            flag  |= O_NONBLOCK;
            fcntl(fd, F_SETFL, flag);
    }
     
    void Server::run()
    {
            char buffer[BUF_SIZE + 1];
            int trigger_num;
            socklen_t addr_len;
            struct sockaddr_in client_addr;
            while(1)
            {
                    trigger_num = epoll_wait(epoll_fd, events, MAX_EVENT, 500);
                    for(int i = 0; i < trigger_num; i++)
                    {
                            //accept new connection
                            if(events[i].data.fd  == listen_fd)
                            {       
                                    addr_len = sizeof(sockaddr);
                                    int connect_fd = accept(listen_fd,(sockaddr *)&client_addr, &addr_len);
                                    set_non_blocking(connect_fd);
                                    connections.insert(connect_fd);
                                    ev.data.fd = connect_fd;
                                    ev.events = EPOLLIN | EPOLLET;
                                    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, connect_fd, &ev);
                                    cout<<"a new connection!"<<endl;
                            }
                            //read from a existed connection
                            else
                            {
                                    size_t read_num = recv(events[i].data.fd, buffer, BUF_SIZE, 0);
                                    if(read_num < 0)
                                    {
                                            perror("read");
                                            return;
                                    }
                                     
                                    if(0 == read_num)
                                    {
                                            auto iterator = connections.find(events[i].data.fd);
                                            if(iterator != connections.end())
                                                    connections.erase(iterator);
                                            cout<<"a connections lost!"<<endl;
                                    }
                                    else
                                            cout<< buffer;
                            }
                    }
            }
    }
     
    int main()
    {
            Server my_server;
            my_server.run();
            return 0;
    }
    

      

      

    四.分析

    优点

    1.可支持打开大量描述符

    在网络应用程序中往往需要对大量文件描述符进行操作,epoll根据机器内存,支持数万到数十万多文件描述符

    2.io效率不随描述符数量增加而线性下降

    因为epoll只关心“活跃”的描述符,不需要对所有描述符进行线性扫描

    缺点

    如实例所示,epoll将对描述符的检测和处理放在同一个循环之中,当处理过程复杂之后,就会使得整个循环变得臃肿,效率会有所下降

    附client.cpp用于测试连接

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<string.h>
    #include<arpa/inet.h>
     
    #define SERV_PORT 5660
    #define BUF_SIZE 1024
     
    int main(int argc, char ** argv)
    {
            char                buffer[BUF_SIZE + 1];
            int                 sockfd;
            struct sockaddr_in  servaddr;
            int                 res;
     
            if(argc != 2)
            {
                    perror("IP address");
                    exit(EXIT_FAILURE);
            }
     
            sockfd = socket(AF_INET, SOCK_STREAM, 0);
     
            bzero(&servaddr, sizeof(servaddr));
            servaddr.sin_family = AF_INET;
            servaddr.sin_port = htons(SERV_PORT);
            inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
     
            connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
     
            while(1)
            {
                    memset(buffer, '', BUF_SIZE);
                    fgets(buffer, BUF_SIZE, stdin);
     
                    if(strcmp(buffer, "end
    ") == 0)
                            break;
     
                    if(strlen(buffer) != 0)
                    {
                            res = write(sockfd, buffer, BUF_SIZE);
                            if(res == -1)
                            {
                                    perror("Write");
                                    exit(EXIT_FAILURE);
                            }
                    }
            }
            exit(EXIT_SUCCESS);
    }
    

      

  • 相关阅读:
    framework7对日历的一些效果处理
    framework7 v2.x轮播图写法:
    soap使用xml调用webapi后返回xml信息进行JSON转换处理,以顺丰查询接口为例
    framework7滑动删除列表触发chrome 报错解决办法
    页面文本超出后CSS实现隐藏的方法
    页面dom事件防止失效的一种写法
    framework7 1.3.5 路由跳转后DOM失效问题
    node 上的cookie的签名和解签名
    VUE在页面没加载完的时候会显示原代码的处理方法
    P2220 [HAOI2012]容易题
  • 原文地址:https://www.cnblogs.com/comoon/p/3830493.html
Copyright © 2011-2022 走看看