epoll小例

引言:## 標題 ##
UNP的第6章講解IO複用的時候詳解了兩個函數:select和poll。我們可以用這兩個接口以單線程的方式處理多個客戶請求。但是,它們都有着自己的缺點。

  1. select最多支持的描述符爲1024(除非你重新編譯內核)
  2. poll雖然沒有上限,但是每次一有風吹草動,它都需要你遍歷整個描述符集。在第一次看這章的時候,我就在想,這樣的時間複雜度是O(N),一定有更加高效的方法!

UNP在第14章的結尾稍稍提起了一下“高級輪詢”。使用這樣的接口可以解決上述的2個問題,但是不用的操作系統對它的實現方式不同,所以這樣的代碼應該被認爲是不可移植的。
對於linux來說,這樣的接口是epoll

學習資料:

先給出epoll的3個學習資料:
手冊:http://man7.org/linux/man-pages/man7/epoll.7.html
例子:https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/
中文:http://blog.csdn.net/xiajun07061225/article/details/9250579

epoll API

以下是我自己寫的小例子,用的是LT模式。

服務器程序:

//utils.h文件可以查看我之前的博客。
#include "../../programe/utils.h"
#include <sys/epoll.h>
#include <stdio.h>
#include <errno.h>
#define MAX_EVENT 64


int main(int argc, char **argv)
{
    if(argc != 2)
    {
        write(2, "port", 4);
        return -1;
    }

    struct epoll_event event, events[MAX_EVENT];//events應該用棧內存還是堆內存?
    int listenfd = tcp_listen(argv[1], 5);
    if(listenfd < 0)
        return -1;

    int epollfd = epoll_create1(0);

    event.data.fd = listenfd;
    event.events = EPOLLIN;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);

    for( ; ; )
    {
        int i,nready;
        nready = epoll_wait(epollfd, events, MAX_EVENT, -1);

        for(i=0;i<nready;++i)
        {
            //new connection
            if(events[i].data.fd == listenfd)//new connection
            {
                int connfd = accept(listenfd, NULL, NULL);
                if(connfd == -1)//accept失敗
                    continue;
                printf("connection accept, fd=%d\n",connfd);
                //將新鏈接加入epoll的監聽。
                event.data.fd = connfd;
                event.events = EPOLLIN;
                epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);

            }else if(events[i].events & EPOLLIN)//data come in
            {
                //接受數據
                ssize_t count;
                char    buff[4096];
                count = recv(events[i].data.fd, buff, sizeof buff, 0);
                printf("read %d character from %d\n", (int)count, events[i].data.fd);

                //處理數據
                if(count == 0)//eof
                {
                    //客戶發完了數據,所以不再需要監聽這個套接字。
                    event.data.fd = events[i].data.fd;
                    epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &event);
                    //shutdown(events[i].data.fd, SHUT_RD);
                    close(events[i].data.fd);//單線程所以直接關閉即可。
                    printf("fd=%d shutdown\n",events[i].data.fd);
                    continue;
                }   
                else if(count < 0)
                {//read失敗,打印出錯時的errno.
                    printf("read error.\n");
                    printf("errno:%d\n",errno);
                    continue;
                }
                else//echo
                {
                    int nsent;
                    while(count)
                    {
                        nsent = send(events[i].data.fd, buff, count,0);
                        count -= nsent;
                    }
                }


            }else
            {
                continue;
            }
        }


    }

    close(listenfd);
    close(epollfd);

}

客戶端程序:

#include "../../programe/utils.h"
#include <sys/select.h>
#include <errno.h>
#include <stdio.h>

int main(int argc, char** argv)
{
    if(argc != 3)
    {
        perror("IP&port\n");
    }

    int sockfd = tcp_connect(argv[1], argv[2]);
    if(sockfd < 0)
        return -1;

    fd_set rset;
    char sendLine[4096],recvLine[4096];
    int n,eof=0;

    for( ; ; )
    {
        bzero(&sendLine, sizeof(sendLine));
        bzero(&recvLine, sizeof(recvLine));

        FD_ZERO(&rset);
        FD_SET(0, &rset);
        FD_SET(sockfd, &rset);

        select(sockfd+1, &rset, NULL, NULL, NULL);

        if(FD_ISSET(0, &rset))
        {
            n = read(0,sendLine,sizeof sendLine);
            if(n>0)
                write(sockfd, sendLine, n);
            else
            {
                if(n == 0)//沒有數據可寫了。
                {
                    shutdown(sockfd, SHUT_WR);
                    eof = 1;        
                }
                else if(n < 0)//發生錯誤,errno?
                    return -1;
            }
        }

        if(FD_ISSET(sockfd, &rset))
        {
            n = read(sockfd, recvLine, 4096); 
            if(n>0)
                write(1, recvLine, n);
            else if(n == 0)
            {
                shutdown(sockfd, SHUT_RD);
                break;
            }
            else//錯誤
            {
                return -1;
            }
        }
            if(eof == 1)
                break;
    }

    close(sockfd);

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