Linux epoll服務器、epoll、poll、select優缺點

epoll、poll和select優缺點

select

優點

(1)select()的可移植性更好,在某些Unix系統上不支持poll()
(2)select() 對於超時值提供了更好的精度:微秒,而poll是毫秒

缺點

(1)每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大
(2)同時每次調用select都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大
(3)select支持的文件描述符數量太小了,默認是1024

poll

優點

(1)poll() 不要求開發者計算最大文件描述符加一的大小。
(2)poll() 在應付大數目的文件描述符的時候相比於select速度更快
(3)它沒有最大連接數的限制,原因是它是基於鏈表來存儲的。

缺點

(1)大量的fd的數組被整體複製於用戶態和內核地址空間之間,而不管這樣的複製是不是有意義。
(2)與select一樣,poll返回後,需要輪詢pollfd來獲取就緒的描述符

epoll

epoll底層實現

在調用epoll_create時操作系統會創建一顆紅黑樹存放socket和一個隊列存放就緒事件

優點

(1)支持一個進程打開大數目的socket描述符(FD)
(2)IO效率不隨FD數目增加而線性下降
(3)使用mmap加速內核與用戶空間的消息傳遞。

epoll服務器實現

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#define MAX_EPOLL_EVENTS 64

static void usage(char* arg)
{
    printf("Usage:%s [local_ip][local_port]\n",arg);
}

int starup(const char* ip,int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0)
    {
        perror("socket");
        exit(2);
    }

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ip);
    int opt = 1;
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

    if(bind(sock,(struct sockaddr*)&server,sizeof(server)) < 0)
    {
        perror("bind");
        exit(3);
    }

    if(listen(sock,5) < 0)
    {
        perror("listen");
        exit(4);
    }

    return sock;
}

int main(int argc,char* argv[])
{
    if(3 != argc)
    {
        usage(argv[0]);
        return 1;
    }

    int listen_sock = starup(argv[1],atoi(argv[2]));
    int epfd = epoll_create(200);
    if(epfd < 0)
    {
        perror("epoll_create");
        return 5;
    }

    struct epoll_event ev,ev_array[MAX_EPOLL_EVENTS];
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;
    if(epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&ev) != 0)
    {
        perror("epoll_ctl");
        return 6;
    }

    while(1)
    {
        int num = epoll_wait(epfd,ev_array,MAX_EPOLL_EVENTS,10000);
        switch(num)
        {
        case -1:
            {
                perror("epoll_wait");
                return 7;
                break;
            }
        case 0:
            {
                printf("nothing ready...\n");
                break;
            }
        default:
            {
                int i;
                for(i = 0;i < num; ++i)
                {
                    if(ev_array[i].data.fd == listen_sock && ev_array[i].events & EPOLLIN)
                    {
                        struct sockaddr_in client;
                        socklen_t len = sizeof(client);
                        int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);
                        if(new_sock < 0)
                        {
                            perror("accept");
                            continue;
                        }

                        printf("get a new client:%s,%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
                        struct epoll_event event;
                        event.events = EPOLLIN | EPOLLOUT;
                        event.data.fd = new_sock;
                        if(epoll_ctl(epfd,EPOLL_CTL_ADD,new_sock,&event) != 0)
                        {
                            perror("epoll_ctl");
                            close(new_sock);
                            continue;
                        }
                    }
                    else if(ev_array[i].events & EPOLLIN)
                    {
                        char buf[1024];
                        ssize_t s = read(ev_array[i].data.fd,buf,sizeof(buf)-1);
                        if(s<0)
                        {
                            perror("read:EPOLLIN");
                            if(epoll_ctl(epfd,EPOLL_CTL_DEL,ev_array[i].data.fd,&ev_array[i])!=0)
                            {
                                perror("epoll_ctl");
                                continue;
                            }
                            close(ev_array[i].data.fd);
                            continue;
                        }
                        else if(s == 0)
                        {
                            printf("client quit...\n");
                            epoll_ctl(epfd,EPOLL_CTL_DEL,ev_array[i].data.fd,&ev_array[i]);
                            close(ev_array[i].data.fd);
                            continue;
                        }
                        else
                        {
                            buf[s]=0;
                            printf("%s\n",buf);
                        }
                    }
                    else if(ev_array[i].events & EPOLLOUT)
                    {
                        char* buf = "hello word";
                        write(ev_array[i].data.fd,buf,strlen(buf));
                        epoll_ctl(epfd,EPOLL_CTL_DEL,ev_array[i].data.fd,&ev_array[i]);
                        close(ev_array[i].data.fd);
                        printf("client is quited\n");
                        continue;
                    }
                }//for
            }
        }//switch
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章