epoll模型實例

一.epoll概述linux

epoll是linux下的一個系統調用,用來監聽大量文件描述符並對其上的觸發事件進行處理。它是select/poll的加強版本,也是linux下多路複用io最經常使用的接口。要理解epoll是什麼,首先得清楚什麼是多路複用io。用戶進行io操做須要通過內核,而若是所請求的io目前不知足條件(如須要從標準輸入讀取數據,而用戶還未輸入),這個時候內核就會讓應用程序陷入等待,即阻塞狀態。我的理解,io複用技術就是經過特定接口,將這種阻塞進行了轉移,轉嫁到了如select/poll/epoll之類多系統調用上,而且支持多個文件描述符多監測,即多路複用。這樣epoll即可以替應用程序同時監聽多個文件描述符,一旦有觸發發生,內核就會通知應用程序進行處理。ios

二.epoll接口服務器

epoll的函數接口既能夠經過查看epoll.h文件來查看,也能夠用man命令查看。這裏引用epoll.h中的描述.(epoll.h在/usr/include/x86_64-linux-gnu/sys目錄下可找到)。網絡

epoll接口主要包括一個結構三個函數socket

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

  

  

四.分析ui

優勢spa

1.可支持打開大量描述符server

在網絡應用程序中每每須要對大量文件描述符進行操做,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, '\0', BUF_SIZE);
                fgets(buffer, BUF_SIZE, stdin);
 
                if(strcmp(buffer, "end\n") == 0)
                        break;
 
                if(strlen(buffer) != 0)
                {
                        res = write(sockfd, buffer, BUF_SIZE);
                        if(res == -1)
                        {
                                perror("Write");
                                exit(EXIT_FAILURE);
                        }
                }
        }
        exit(EXIT_SUCCESS);
}
相關文章
相關標籤/搜索