<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    服務器 libevent中epoll使用實例demo

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    名詞解釋:man epoll之后,得到如下結果:

    NAME
           epoll - I/O event notification facility

    SYNOPSIS
           #include <sys/epoll.h>

    DESCRIPTION
           epoll is a variant of poll(2) that can be used either as Edge or Level
           Triggered interface and scales well to large numbers of  watched  fds.
           Three  system  calls  are provided to set up and control an epoll set:
           epoll_create(2), epoll_ctl(2), epoll_wait(2).

           An epoll set is connected to a file descriptor created  by  epoll_cre-
           ate(2).   Interest for certain file descriptors is then registered via
           epoll_ctl(2).  Finally, the actual wait is started by epoll_wait(2).

    其實,一切的解釋都是多余的,按照我目前的了解,EPOLL模型似乎只有一種格式,所以大家只要參考我下面的代碼,就能夠對EPOLL有所了解了,代碼的解釋都已經在注釋中:

    while (TRUE)
     {
      int nfds = epoll_wait (m_epoll_fd, m_events, MAX_EVENTS, EPOLL_TIME_OUT);//等待EPOLL時間的發生,相當于監聽,至于相關的端口,需要在初始化EPOLL的時候綁定。
      if (nfds <= 0)
       continue;
      m_bOnTimeChecking = FALSE;
      G_CurTime = time(NULL);
      for (int i=0; i<nfds; i++)
      {
       try
       {
        if (m_events[i].data.fd == m_listen_http_fd)//如果新監測到一個HTTP用戶連接到綁定的HTTP端口,建立新的連接。由于我們新采用了SOCKET連接,所以基本沒用。
        {
         OnAcceptHttpEpoll ();
        }
        else if (m_events[i].data.fd == m_listen_sock_fd)//如果新監測到一個SOCKET用戶連接到了綁定的SOCKET端口,建立新的連接。
        {
         OnAcceptSockEpoll ();
        }
        else if (m_events[i].events & EPOLLIN)//如果是已經連接的用戶,并且收到數據,那么進行讀入。
        {
         OnReadEpoll (i);
        }

        OnWriteEpoll (i);//查看當前的活動連接是否有需要寫出的數據。
       }
       catch (int)
       {
        PRINTF ("CATCH捕獲錯誤/n");
        continue;
       }
      }
      m_bOnTimeChecking = TRUE;
      OnTimer ();//進行一些定時的操作,主要就是刪除一些短線用戶等。
     }

     其實EPOLL的精華,按照我目前的理解,也就是上述的幾段短短的代碼,看來時代真的不同了,以前如何接受大量用戶連接的問題,現在卻被如此輕松的搞定,真是讓人不得不感嘆。

    今天搞了一天的epoll,想做一個高并發的代理程序。剛開始真是郁悶,一直搞不通,網上也有幾篇介紹epoll的文章。但都不深入,沒有將一些注意的地方講明。以至于走了很多彎路,現將自己的一些理解共享給大家,以少走彎路。 

    epoll用到的所有函數都是在頭文件sys/epoll.h中聲明,有什么地方不明白或函數忘記了可以去看一下。 
    epoll和select相比,最大不同在于: 

    1epoll返回時已經明確的知道哪個sokcet fd發生了事件,不用再一個個比對。這樣就提高了效率。 
    2select的FD_SETSIZE是有限止的,而epoll是沒有限止的只與系統資源有關。 

    1、epoll_create函數
    函數聲明:int epoll_create(int size)
    該函數生成一個epoll專用的文件描述符。它其實是在內核申請一空間,用來存放你想關注的socket fd上是否發生以及發生了什么事件。size就是你在這個epoll fd上能關注的最大socket fd數。隨你定好了。只要你有空間。可參見上面與select之不同2. 

    22、epoll_ctl函數 
    函數聲明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 
    該函數用于控制某個epoll文件描述符上的事件,可以注冊事件,修改事件,刪除事件。 
    參數: 
    epfd:由 epoll_create 生成的epoll專用的文件描述符; 
    op:要進行的操作例如注冊事件,可能的取值EPOLL_CTL_ADD 注冊、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 刪除 

    fd:關聯的文件描述符; 
    event:指向epoll_event的指針; 
    如果調用成功返回0,不成功返回-1 

    用到的數據結構 
    typedef union epoll_data { 
    void *ptr;
    int fd;
    __uint32_t u32;
    __uint64_t u64;
    } epoll_data_t; 

    struct epoll_event {
    __uint32_t events; /* Epoll events */
    epoll_data_t data; /* User data variable */
    };


    如: 
    struct epoll_event ev;
    //設置與要處理的事件相關的文件描述符
    ev.data.fd=listenfd;
    //設置要處理的事件類型
    ev.events=EPOLLIN|EPOLLET;
    //注冊epoll事件
    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);


    常用的事件類型:
    EPOLLIN :表示對應的文件描述符可以讀;
    EPOLLOUT:表示對應的文件描述符可以寫;
    EPOLLPRI:表示對應的文件描述符有緊急的數據可讀
    EPOLLERR:表示對應的文件描述符發生錯誤;
    EPOLLHUP:表示對應的文件描述符被掛斷;
    EPOLLET:表示對應的文件描述符有事件發生;


    3、epoll_wait函數
    函數聲明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
    該函數用于輪詢I/O事件的發生;
    參數:
    epfd:由epoll_create 生成的epoll專用的文件描述符;
    epoll_event:用于回傳代處理事件的數組;
    maxevents:每次能處理的事件數;
    timeout:等待I/O事件發生的超時值(單位我也不太清楚);-1相當于阻塞,0相當于非阻塞。一般用-1即可
    返回發生事件數。

     

     

     

     

    [java] view plaincopy  
    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <errno.h>  
    4. #include <string.h>  
    5. #include <sys/types.h>  
    6. #include <netinet/in.h>  
    7. #include <sys/socket.h>  
    8. #include <sys/wait.h>  
    9. #include <unistd.h>  
    10. #include <arpa/inet.h>  
    11. #include <openssl/ssl.h>  
    12. #include <openssl/err.h>  
    13. #include <fcntl.h>  
    14. #include <sys/epoll.h>  
    15. #include <sys/time.h>  
    16. #include <sys/resource.h>  
    [java] view plaincopy  
    1. #define MAXBUF 1024  
    2. #define MAXEPOLLSIZE 10000  
    [java] view plaincopy  
    1. /* 
    2. setnonblocking - 設置句柄為非阻塞方式 
    3. */  
    4. int setnonblocking(int sockfd)  
    5. {  
    6.     if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)  
    7.  {  
    8.         return -1;  
    9.     }  
    10.     return 0;  
    11. }  
    [java] view plaincopy  
    1. /* 
    2. handle_message - 處理每個 socket 上的消息收發 
    3. */  
    4. int handle_message(int new_fd)  
    5. {  
    6.     char buf[MAXBUF + 1];  
    7.     int len;  
    8.       
    9.  /* 開始處理每個新連接上的數據收發 */  
    10.     bzero(buf, MAXBUF + 1);  
    11.      
    12.  /* 接收客戶端的消息 */  
    13.     len = recv(new_fd, buf, MAXBUF, 0);  
    14.     if (len > 0)  
    15.  {  
    16.         printf  
    17.             ("%d接收消息成功:'%s',共%d個字節的數據/n",  
    18.              new_fd, buf, len);  
    19.  }  
    20.     else  
    21.  {  
    22.         if (len < 0)  
    23.      printf  
    24.                 ("消息接收失敗!錯誤代碼是%d,錯誤信息是'%s'/n",  
    25.                  errno, strerror(errno));  
    26.         close(new_fd);  
    27.         return -1;  
    28.     }  
    29.     /* 處理每個新連接上的數據收發結束 */  
    30.     return len;  
    31. }  
    32. /************關于本文檔******************************************** 
    33. *filename: epoll-server.c 
    34. *purpose: 演示epoll處理海量socket連接的方法 
    35. *wrote by: zhoulifa(<a href="mailto:zhoulifa@163.com">zhoulifa@163.com</a>) 周立發(<a >http://zhoulifa.bokee.com</a>) 
    36. Linux愛好者 Linux知識傳播者 SOHO族 開發者 最擅長C語言 
    37. *date time:2007-01-31 21:00 
    38. *Note: 任何人可以任意復制代碼并運用這些文檔,當然包括你的商業用途 
    39. * 但請遵循GPL 
    40. *Thanks to:Google 
    41. *Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力 
    42. * 科技站在巨人的肩膀上進步更快!感謝有開源前輩的貢獻! 
    43. *********************************************************************/  
    44. int main(int argc, char **argv)  
    45. {  
    46.     int listener, new_fd, kdpfd, nfds, n, ret, curfds;  
    47.     socklen_t len;  
    48.     struct sockaddr_in my_addr, their_addr;  
    49.     unsigned int myport, lisnum;  
    50.     struct epoll_event ev;  
    51.     struct epoll_event events[MAXEPOLLSIZE];  
    52.     struct rlimit rt;  
    53.  myport = 5000;  
    54.  lisnum = 2;   
    55.       
    56.  /* 設置每個進程允許打開的最大文件數 */  
    57.     rt.rlim_max = rt.rlim_cur = MAXEPOLLSIZE;  
    58.     if (setrlimit(RLIMIT_NOFILE, &rt) == -1)   
    59.  {  
    60.         perror("setrlimit");  
    61.         exit(1);  
    62.     }  
    63.     else   
    64.     {  
    65.         printf("設置系統資源參數成功!/n");  
    66.     }  
    [java] view plaincopy  
    1.     /* 開啟 socket 監聽 */  
    2.     if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)  
    3.     {  
    4.         perror("socket");  
    5.         exit(1);  
    6.     }  
    7.     else  
    8.     {  
    9.         printf("socket 創建成功!/n");  
    10.  }  
    11.    
    12.     setnonblocking(listener);  
    [java] view plaincopy  
    1.     bzero(&my_addr, sizeof(my_addr));  
    2.     my_addr.sin_family = PF_INET;  
    3.     my_addr.sin_port = htons(myport);  
    4.     my_addr.sin_addr.s_addr = INADDR_ANY;  
    [java] view plaincopy  
    1.     if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)   
    2.     {  
    3.         perror("bind");  
    4.         exit(1);  
    5.     }   
    6.     else  
    7.     {  
    8.         printf("IP 地址和端口綁定成功/n");  
    9.  }  
    10.     if (listen(listener, lisnum) == -1)   
    11.     {  
    12.         perror("listen");  
    13.         exit(1);  
    14.     }  
    15.     else  
    16.     {  
    17.         printf("開啟服務成功!/n");  
    18.  }  
    19.       
    20.  /* 創建 epoll 句柄,把監聽 socket 加入到 epoll 集合里 */  
    21.     kdpfd = epoll_create(MAXEPOLLSIZE);  
    22.     len = sizeof(struct sockaddr_in);  
    23.     ev.events = EPOLLIN | EPOLLET;  
    24.     ev.data.fd = listener;  
    25.     if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0)   
    26.  {  
    27.         fprintf(stderr, "epoll set insertion error: fd=%d/n", listener);  
    28.         return -1;  
    29.     }  
    30.  else  
    31.     {  
    32.   printf("監聽 socket 加入 epoll 成功!/n");  
    33.  }  
    34.     curfds = 1;  
    35.     while (1)   
    36.  {  
    37.         /* 等待有事件發生 */  
    38.         nfds = epoll_wait(kdpfd, events, curfds, -1);  
    39.         if (nfds == -1)  
    40.   {  
    41.             perror("epoll_wait");  
    42.             break;  
    43.         }  
    44.         /* 處理所有事件 */  
    45.         for (n = 0; n < nfds; ++n)  
    46.   {  
    47.             if (events[n].data.fd == listener)   
    48.    {  
    49.                 new_fd = accept(listener, (struct sockaddr *) &their_addr,&len);  
    50.                 if (new_fd < 0)   
    51.     {  
    52.                     perror("accept");  
    53.                     continue;  
    54.                 }   
    55.     else  
    56.     {  
    57.      printf("有連接來自于: %d:%d, 分配的 socket 為:%d/n",  
    58.                              inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);  
    59.     }  
    60.                 setnonblocking(new_fd);  
    61.                 ev.events = EPOLLIN | EPOLLET;  
    62.                 ev.data.fd = new_fd;  
    63.                 if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, new_fd, &ev) < 0)  
    64.     {  
    65.                     fprintf(stderr, "把 socket '%d' 加入 epoll 失敗!%s/n",  
    66.                             new_fd, strerror(errno));  
    67.                     return -1;  
    68.                 }  
    69.                 curfds++;  
    70.             }   
    71.    else  
    72.    {  
    73.                 ret = handle_message(events[n].data.fd);  
    74.                 if (ret < 1 && errno != 11)  
    75.     {  
    76.                     epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd,&ev);  
    77.                     curfds--;  
    78.                 }  
    79.             }  
    80.         }  
    81.     }  
    82.     close(listener);  
    83.     return 0;  
    84. }  

     epoll_wait運行的原理是 等侍注冊在epfd上的socket fd的事件的發生,如果發生則將發生的sokct fd和事件類型放入到events數組中。 并且將注冊在epfd上的socket fd的事件類型給清空,所以如果下一個循環你還要關注這個socket fd的話,則需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)來重新設置socket fd的事件類型。這時不用EPOLL_CTL_ADD,因為socket fd并未清空,只是事件類型清空。這一步非常重要。 

     

    二、第二個示例:

     

    1. Epoll是何方神圣?

    Epoll可是當前在Linux下開發大規模并發網絡程序的熱門人選,Epoll 在Linux2.6內核中正式引入,和select相似,其實都I/O多路復用技術而已,并沒有什么神秘的。

    其實在Linux下設計并發網絡程序,向來不缺少方法,比如典型的Apache模型(Process Per Connection,簡稱PPC),TPC(Thread PerConnection)模型,以及select模型和poll模型,那為何還要再引入Epoll這個東東呢?那還是有得說說的…

    2. 常用模型的缺點

    如果不擺出來其他模型的缺點,怎么能對比出Epoll的優點呢。

    2.1 PPC/TPC模型

    這兩種模型思想類似,就是讓每一個到來的連接一邊自己做事去,別再來煩我。只是PPC是為它開了一個進程,而TPC開了一個線程。可是別煩我是有代價的,它要時間和空間啊,連接多了之后,那么多的進程/線程切換,這開銷就上來了;因此這類模型能接受的最大連接數都不會高,一般在幾百個左右。

    2.2 select模型

    1. 最大并發數限制,因為一個進程所打開的FD(文件描述符)是有限制的,由FD_SETSIZE設置,默認值是1024/2048,因此Select模型的最大并發數就被相應限制了。自己改改這個FD_SETSIZE?想法雖好,可是先看看下面吧…

    2. 效率問題,select每次調用都會線性掃描全部的FD集合,這樣效率就會呈現線性下降,把FD_SETSIZE改大的后果就是,大家都慢慢來,什么?都超時了??!!

    3. 內核/用戶空間 內存拷貝問題,如何讓內核把FD消息通知給用戶空間呢?在這個問題上select采取了內存拷貝方法。

    2.3 poll模型

    基本上效率和select是相同的,select缺點的2和3它都沒有改掉。

    3. Epoll的提升

    把其他模型逐個批判了一下,再來看看Epoll的改進之處吧,其實把select的缺點反過來那就是Epoll的優點了。

    3.1. Epoll沒有最大并發連接的限制,上限是最大可以打開文件的數目,這個數字一般遠大于2048, 一般來說這個數目和系統內存關系很大,具體數目可以cat /proc/sys/fs/file-max察看。

    3.2. 效率提升,Epoll最大的優點就在于它只管你“活躍”的連接,而跟連接總數無關,因此在實際的網絡環境中,Epoll的效率就會遠遠高于select和poll。

    3.3. 內存拷貝,Epoll在這點上使用了“共享內存”,這個內存拷貝也省略了。

     

     

    4. Epoll為什么高效

    Epoll的高效和其數據結構的設計是密不可分的,這個下面就會提到。

    首先回憶一下select模型,當有I/O事件到來時,select通知應用程序有事件到了快去處理,而應用程序必須輪詢所有的FD集合,測試每個FD是否有事件發生,并處理事件;代碼像下面這樣:

     

    int res = select(maxfd+1, &readfds, NULL, NULL, 120);

    if(res > 0)

    {

        for(int i = 0; i < MAX_CONNECTION; i++)

        {

            if(FD_ISSET(allConnection[i],&readfds))

            {

                handleEvent(allConnection[i]);

            }

        }

    }

    // if(res == 0) handle timeout, res < 0 handle error

     

    Epoll不僅會告訴應用程序有I/0事件到來,還會告訴應用程序相關的信息,這些信息是應用程序填充的,因此根據這些信息應用程序就能直接定位到事件,而不必遍歷整個FD集合。

    intres = epoll_wait(epfd, events, 20, 120);

    for(int i = 0; i < res;i++)

    {

        handleEvent(events[n]);

    }

    5. Epoll關鍵數據結構

    前面提到Epoll速度快和其數據結構密不可分,其關鍵數據結構就是:

    structepoll_event {

        __uint32_t events;      // Epoll events

        epoll_data_t data;      // User datavariable

    };

    typedef union epoll_data {

        void *ptr;

       int fd;

        __uint32_t u32;

        __uint64_t u64;

    } epoll_data_t;

    可見epoll_data是一個union結構體,借助于它應用程序可以保存很多類型的信息:fd、指針等等。有了它,應用程序就可以直接定位目標了。

    6. 使用Epoll

    既然Epoll相比select這么好,那么用起來如何呢?會不會很繁瑣啊…先看看下面的三個函數吧,就知道Epoll的易用了。

     

    intepoll_create(int size);

    生成一個Epoll專用的文件描述符,其實是申請一個內核空間,用來存放你想關注的socket fd上是否發生以及發生了什么事件。size就是你在這個Epoll fd上能關注的最大socket fd數,大小自定,只要內存足夠。

    intepoll_ctl(int epfd, intop, int fd, structepoll_event *event);

    控制某個Epoll文件描述符上的事件:注冊、修改、刪除。其中參數epfd是epoll_create()創建Epoll專用的文件描述符。相對于select模型中的FD_SET和FD_CLR宏。

    intepoll_wait(int epfd,structepoll_event * events,int maxevents,int timeout);

    等待I/O事件的發生;參數說明:

    epfd:由epoll_create() 生成的Epoll專用的文件描述符;

    epoll_event:用于回傳代處理事件的數組;

    maxevents:每次能處理的事件數;

    timeout:等待I/O事件發生的超時值;

    返回發生事件數。

    相對于select模型中的select函數。

    7. 例子程序

    下面是一個簡單Echo Server的例子程序,麻雀雖小,五臟俱全,還包含了一個簡單的超時檢查機制,簡潔起見沒有做錯誤處理。

    [cpp] view plaincopy
    1. //     
    2. // a simple echo server using epoll in linux    
    3. //     
    4. // 2009-11-05    
    5. // 2013-03-22:修改了幾個問題,1是/n格式問題,2是去掉了原代碼不小心加上的ET模式;  
    6. // 本來只是簡單的示意程序,決定還是加上 recv/send時的buffer偏移  
    7. // by sparkling    
    8. //     
    9. #include <sys/socket.h>    
    10. #include <sys/epoll.h>    
    11. #include <netinet/in.h>    
    12. #include <arpa/inet.h>    
    13. #include <fcntl.h>    
    14. #include <unistd.h>    
    15. #include <stdio.h>    
    16. #include <errno.h>    
    17. #include <iostream>    
    18. using namespace std;    
    19. #define MAX_EVENTS 500    
    20. struct myevent_s    
    21. {    
    22.     int fd;    
    23.     void (*call_back)(int fd, int events, void *arg);    
    24.     int events;    
    25.     void *arg;    
    26.     int status; // 1: in epoll wait list, 0 not in    
    27.     char buff[128]; // recv data buffer    
    28.     int len, s_offset;    
    29.     long last_active; // last active time    
    30. };    
    31. // set event    
    32. void EventSet(myevent_s *ev, int fd, void (*call_back)(int, int, void*), void *arg)    
    33. {    
    34.     ev->fd = fd;    
    35.     ev->call_back = call_back;    
    36.     ev->events = 0;    
    37.     ev->arg = arg;    
    38.     ev->status = 0;  
    39.     bzero(ev->buff, sizeof(ev->buff));  
    40.     ev->s_offset = 0;    
    41.     ev->len = 0;  
    42.     ev->last_active = time(NULL);    
    43. }    
    44. // add/mod an event to epoll    
    45. void EventAdd(int epollFd, int events, myevent_s *ev)    
    46. {    
    47.     struct epoll_event epv = {0, {0}};    
    48.     int op;    
    49.     epv.data.ptr = ev;    
    50.     epv.events = ev->events = events;    
    51.     if(ev->status == 1){    
    52.         op = EPOLL_CTL_MOD;    
    53.     }    
    54.     else{    
    55.         op = EPOLL_CTL_ADD;    
    56.         ev->status = 1;    
    57.     }    
    58.     if(epoll_ctl(epollFd, op, ev->fd, &epv) < 0)    
    59.         printf("Event Add failed[fd=%d], evnets[%d]\n", ev->fd, events);    
    60.     else    
    61.         printf("Event Add OK[fd=%d], op=%d, evnets[%0X]\n", ev->fd, op, events);    
    62. }    
    63. // delete an event from epoll    
    64. void EventDel(int epollFd, myevent_s *ev)    
    65. {    
    66.     struct epoll_event epv = {0, {0}};    
    67.     if(ev->status != 1) return;    
    68.     epv.data.ptr = ev;    
    69.     ev->status = 0;  
    70.     epoll_ctl(epollFd, EPOLL_CTL_DEL, ev->fd, &epv);    
    71. }    
    72. int g_epollFd;    
    73. myevent_s g_Events[MAX_EVENTS+1]; // g_Events[MAX_EVENTS] is used by listen fd    
    74. void RecvData(int fd, int events, void *arg);    
    75. void SendData(int fd, int events, void *arg);    
    76. // accept new connections from clients    
    77. void AcceptConn(int fd, int events, void *arg)    
    78. {    
    79.     struct sockaddr_in sin;    
    80.     socklen_t len = sizeof(struct sockaddr_in);    
    81.     int nfd, i;    
    82.     // accept    
    83.     if((nfd = accept(fd, (struct sockaddr*)&sin, &len)) == -1)    
    84.     {    
    85.         if(errno != EAGAIN && errno != EINTR)    
    86.         {    
    87.         }  
    88.         printf("%s: accept, %d", __func__, errno);    
    89.         return;    
    90.     }    
    91.     do    
    92.     {    
    93.         for(i = 0; i < MAX_EVENTS; i++)    
    94.         {    
    95.             if(g_Events[i].status == 0)    
    96.             {    
    97.                 break;    
    98.             }    
    99.         }    
    100.         if(i == MAX_EVENTS)    
    101.         {    
    102.             printf("%s:max connection limit[%d].", __func__, MAX_EVENTS);    
    103.             break;    
    104.         }    
    105.         // set nonblocking  
    106.         int iret = 0;  
    107.         if((iret = fcntl(nfd, F_SETFL, O_NONBLOCK)) < 0)  
    108.         {  
    109.             printf("%s: fcntl nonblocking failed:%d", __func__, iret);  
    110.             break;  
    111.         }  
    112.         // add a read event for receive data    
    113.         EventSet(&g_Events[i], nfd, RecvData, &g_Events[i]);    
    114.         EventAdd(g_epollFd, EPOLLIN, &g_Events[i]);    
    115.     }while(0);    
    116.     printf("new conn[%s:%d][time:%d], pos[%d]\n", inet_ntoa(sin.sin_addr),   
    117.             ntohs(sin.sin_port), g_Events[i].last_active, i);    
    118. }    
    119. // receive data    
    120. void RecvData(int fd, int events, void *arg)    
    121. {    
    122.     struct myevent_s *ev = (struct myevent_s*)arg;    
    123.     int len;    
    124.     // receive data  
    125.     len = recv(fd, ev->buff+ev->len, sizeof(ev->buff)-1-ev->len, 0);      
    126.     EventDel(g_epollFd, ev);  
    127.     if(len > 0)  
    128.     {  
    129.         ev->len += len;  
    130.         ev->buff[len] = '\0';    
    131.         printf("C[%d]:%s\n", fd, ev->buff);    
    132.         // change to send event    
    133.         EventSet(ev, fd, SendData, ev);    
    134.         EventAdd(g_epollFd, EPOLLOUT, ev);    
    135.     }    
    136.     else if(len == 0)    
    137.     {    
    138.         close(ev->fd);    
    139.         printf("[fd=%d] pos[%d], closed gracefully.\n", fd, ev-g_Events);    
    140.     }    
    141.     else    
    142.     {    
    143.         close(ev->fd);    
    144.         printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));    
    145.     }    
    146. }    
    147. // send data    
    148. void SendData(int fd, int events, void *arg)    
    149. {    
    150.     struct myevent_s *ev = (struct myevent_s*)arg;    
    151.     int len;    
    152.     // send data    
    153.     len = send(fd, ev->buff + ev->s_offset, ev->len - ev->s_offset, 0);  
    154.     if(len > 0)    
    155.     {  
    156.         printf("send[fd=%d], [%d<->%d]%s\n", fd, len, ev->len, ev->buff);  
    157.         ev->s_offset += len;  
    158.         if(ev->s_offset == ev->len)  
    159.         {  
    160.             // change to receive event  
    161.             EventDel(g_epollFd, ev);    
    162.             EventSet(ev, fd, RecvData, ev);    
    163.             EventAdd(g_epollFd, EPOLLIN, ev);    
    164.         }  
    165.     }    
    166.     else    
    167.     {    
    168.         close(ev->fd);    
    169.         EventDel(g_epollFd, ev);    
    170.         printf("send[fd=%d] error[%d]\n", fd, errno);    
    171.     }    
    172. }    
    173. void InitListenSocket(int epollFd, short port)    
    174. {    
    175.     int listenFd = socket(AF_INET, SOCK_STREAM, 0);    
    176.     fcntl(listenFd, F_SETFL, O_NONBLOCK); // set non-blocking    
    177.     printf("server listen fd=%d\n", listenFd);    
    178.     EventSet(&g_Events[MAX_EVENTS], listenFd, AcceptConn, &g_Events[MAX_EVENTS]);    
    179.     // add listen socket    
    180.     EventAdd(epollFd, EPOLLIN, &g_Events[MAX_EVENTS]);    
    181.     // bind & listen    
    182.     sockaddr_in sin;    
    183.     bzero(&sin, sizeof(sin));    
    184.     sin.sin_family = AF_INET;    
    185.     sin.sin_addr.s_addr = INADDR_ANY;    
    186.     sin.sin_port = htons(port);    
    187.     bind(listenFd, (const sockaddr*)&sin, sizeof(sin));    
    188.     listen(listenFd, 5);    
    189. }    
    190. int main(int argc, char **argv)    
    191. {    
    192.     unsigned short port = 12345; // default port    
    193.     if(argc == 2){    
    194.         port = atoi(argv[1]);    
    195.     }    
    196.     // create epoll    
    197.     g_epollFd = epoll_create(MAX_EVENTS);    
    198.     if(g_epollFd <= 0) printf("create epoll failed.%d\n", g_epollFd);    
    199.     // create & bind listen socket, and add to epoll, set non-blocking    
    200.     InitListenSocket(g_epollFd, port);    
    201.     // event loop    
    202.     struct epoll_event events[MAX_EVENTS];    
    203.     printf("server running:port[%d]\n", port);    
    204.     int checkPos = 0;    
    205.     while(1){    
    206.         // a simple timeout check here, every time 100, better to use a mini-heap, and add timer event    
    207.         long now = time(NULL);    
    208.         for(int i = 0; i < 100; i++, checkPos++) // doesn't check listen fd    
    209.         {    
    210.             if(checkPos == MAX_EVENTS) checkPos = 0; // recycle    
    211.             if(g_Events[checkPos].status != 1) continue;    
    212.             long duration = now - g_Events[checkPos].last_active;    
    213.             if(duration >= 60) // 60s timeout    
    214.             {    
    215.                 close(g_Events[checkPos].fd);    
    216.                 printf("[fd=%d] timeout[%d--%d].\n", g_Events[checkPos].fd, g_Events[checkPos].last_active, now);    
    217.                 EventDel(g_epollFd, &g_Events[checkPos]);    
    218.             }    
    219.         }    
    220.         // wait for events to happen    
    221.         int fds = epoll_wait(g_epollFd, events, MAX_EVENTS, 1000);    
    222.         if(fds < 0){    
    223.             printf("epoll_wait error, exit\n");    
    224.             break;    
    225.         }    
    226.         for(int i = 0; i < fds; i++){    
    227.             myevent_s *ev = (struct myevent_s*)events[i].data.ptr;    
    228.             if((events[i].events&EPOLLIN)&&(ev->events&EPOLLIN)) // read event    
    229.             {    
    230.                 ev->call_back(ev->fd, events[i].events, ev->arg);    
    231.             }    
    232.             if((events[i].events&EPOLLOUT)&&(ev->events&EPOLLOUT)) // write event    
    233.             {    
    234.                 ev->call_back(ev->fd, events[i].events, ev->arg);    
    235.             }    
    236.         }    
    237.     }    
    238.     // free resource    
    239.     return 0;    
    240. }     
    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全