1. 程式人生 > >libevent介紹及示例

libevent介紹及示例

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <netinet/in.h>
  5. #include <netinet/tcp.h>
  6. #include <event.h>
  7. #include <sys/types.h>
  8. #include <sys/socket.h>
  9. #include <errno.h>
  10. #include <fcntl.h>
  11. static short ListenPort = 8080;
  12. static long ListenAddr =
     INADDR_ANY;//任意地址的值就是0
  13. static int MaxConnections = 1024;
  14. static int ServerSocket;
  15. static struct event ServerEvent;//建立event
  16. //不論在什麼平臺編寫網路程式,都應該使用NONBLOCK將一個socket設定成非阻塞模式。這樣可以保證你的程式至少不會在recv/send/accept/connect這些操作上發生block從而將整個網路服務都停下來
  17. int SetNonblock(int fd)
  18. {
  19.     int flags;
  20.     if ((flags = fcntl(fd, F_GETFL)
    ) == -1) { //用來操作檔案描述符的一些特性
  21.         return -1;
  22.     }
  23.     if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
  24.         return -1;
  25.     }
  26.     return 0;
  27. }
  28. //這個函式當客戶端的socket可讀時由libevent呼叫
  29. void ServerRead(int fd, short ev, void *arg)
  30. {
  31.     struct client *client = (struct client *)arg;
  32.     u_char buf[8196];
  33.     int
     len, wlen;
  34.     //會把引數fd 所指的檔案傳送count個位元組到buf指標所指的記憶體中
  35.     len = read(fd, buf, sizeof(buf));
  36.     if (len == 0) {
  37.         /* 客戶端斷開連線,在這裡移除讀事件並且釋放客戶資料結構 */
  38.         printf("disconnected\n");
  39.         close(fd);
  40.         event_del(&ServerEvent);
  41.         free(client);
  42.         return;
  43.     } else if (len < 0) {
  44.         /* 出現了其它的錯誤,在這裡關閉socket,移除事件並且釋放客戶資料結構 */
  45.         printf("socket fail %s\n", strerror(errno));
  46.         close(fd);
  47.         event_del(&ServerEvent);
  48.         free(client);
  49.         return;
  50.     }
  51.     /* 
  52.      為了簡便,我們直接將資料寫回到客戶端。通常我們不能在非阻塞的應用程式中這麼做,
  53.        我們應該將資料放到佇列中,等待可寫事件的時候再寫回客戶端。 
  54.      如果使用多個終端進行socket連線會出現錯誤socket fail Bad file descriptor
  55.      */
  56.     wlen = write(fd, buf, len);
  57.     if (wlen < len) {
  58.         printf("not all data write back to client\n");
  59.     }
  60.     return;
  61. }
  62. /*
  63.    當有一個連線請求準備被接受時,這個函式將被libevent呼叫並傳遞給三個變數: 
  64.    int fd:觸發事件的檔案描述符. 
  65.    short event:觸發事件的型別EV_TIMEOUT,EV_SIGNAL, EV_READ, or EV_WRITE. 
  66.    void* :由arg引數指定的變數. 
  67. */
  68. void ServerAccept(int fd, short ev, void *arg)
  69. {
  70.     int cfd;
  71.     struct sockaddr_in addr;
  72.     socklen_t addrlen = sizeof(addr);
  73.     int yes = 1;
  74.     int retval;
  75.     //將從連線請求佇列中獲得連線資訊,建立新的套接字,並返回該套接字的檔案描述符。
  76.     //新建立的套接字用於伺服器與客戶機的通訊,而原來的套接字仍然處於監聽狀態。
  77.     //該函式的第一個引數指定處於監聽狀態的流套接字
  78.     cfd = accept(fd, (struct sockaddr *)&addr, &addrlen);
  79.     if (cfd == -1) {
  80.         printf("accept(): can not accept client connection");
  81.         return;
  82.     }
  83.     if (SetNonblock(cfd) == -1) {
  84.         close(cfd);
  85.         return;
  86.     }
  87.     //設定與某個套接字關聯的選項
  88.     //引數二 IPPROTO_TCP:TCP選項
  89.     //引數三 TCP_NODELAY 不使用Nagle演算法 選擇立即傳送資料而不是等待產生更多的資料然後再一次傳送
  90.     // 更多引數TCP_NODELAY 和 TCP_CORK
  91.     //引數四 新選項TCP_NODELAY的值
  92.     if (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
  93.         printf("setsockopt(): TCP_NODELAY %s\n", strerror(errno));
  94.         close(cfd);
  95.         return;
  96.     }
  97.     event_set(&ServerEvent, cfd, EV_READ | EV_PERSIST, ServerRead, NULL);
  98.     event_add(&ServerEvent, NULL);
  99.     printf("Accepted connection from %s\n", inet_ntoa(addr.sin_addr));
  100. }
  101. int NewSocket(void)
  102. {
  103.     struct sockaddr_in sa;
  104.     //socket函式來建立一個能夠進行網路通訊的套接字。
  105.     //第一個引數指定應用程式使用的通訊協議的協議族,對於TCP/IP協議族,該引數置AF_INET;
  106.     //第二個引數指定要建立的套接字型別
  107.     //流套接字型別為SOCK_STREAM、資料報套接字型別為SOCK_DGRAM、原始套接字SOCK_RAW
  108.     //第三個引數指定應用程式所使用的通訊協議。
  109.     ServerSocket = socket(AF_INET, SOCK_STREAM, 0);
  110.     if (ServerSocket == -1) {
  111.         printf("socket(): can not create server socket\n");
  112.         return -1;
  113.     }
  114.     if (SetNonblock(ServerSocket) == -1) {
  115.         return -1;
  116.     }
  117.     //清空記憶體資料
  118.     memset(&sa, 0, sizeof(sa));
  119.     sa.sin_family = AF_INET;
  120.     //htons將一個無符號短整型數值轉換為網路位元組序
  121.     sa.sin_port = htons(ListenPort);
  122.     //htonl將主機的無符號長整形數轉換成網路位元組順序
  123.     sa.sin_addr.s_addr = htonl(ListenAddr);
  124.     //(struct sockaddr*)&sa將sa強制轉換為sockaddr型別的指標
  125.     /*struct sockaddr 
  126.         資料結構用做bind、connect、recvfrom、sendto等函式的引數,指明地址資訊。
  127.         但一般程式設計中並不直接針對此資料結構操作,而是使用另一個與sockaddr等價的資料結構 struct sockaddr_in
  128.         sockaddr_in和sockaddr是並列的結構,指向sockaddr_in的結構體的指標也可以指向
  129.         sockadd的結構體,並代替它。也就是說,你可以使用sockaddr_in建立你所需要的資訊,
  130.         在最後用進行型別轉換就可以了
  131.     */
  132.     //bind函式用於將套接字繫結到一個已知的地址上
  133.     if (bind(ServerSocket, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
  134.         close(ServerSocket);
  135.         printf("bind(): can not bind server socket");
  136.         return -1;
  137.     }
  138.     //執行listen 之後套接字進入被動模式
  139.     //MaxConnections 連線請求佇列的最大長度,佇列滿了以後,將拒絕新的連線請求
  140.     if (listen(ServerSocket, MaxConnections) == -1) {
  141.         printf("listen(): can not listen server socket");
  142.         close(ServerSocket);
  143.         return -1;
  144.     }
  145.     /*
  146.      event_set的引數:
  147.      + 引數1: 為要建立的event
  148.      + 引數2: file descriptor,建立純計時器可以設定其為-1,即巨集evtimer_set定義的那樣
  149.      + 引數3: 設定event種類,常用的EV_READ, EV_WRITE, EV_PERSIST, EV_SIGNAL, EV_TIMEOUT,純計時器設定該引數為0
  150.      + 引數4: event被啟用之後觸發的callback函式
  151.      + 引數5: 傳遞給callback函式的引數
  152.      備註:
  153.             如果初始化event的時候設定其為persistent的(設定了EV_PERSIST)
  154.             則使用event_add將其新增到偵聽事件集合後(pending狀態)
  155.             該event會持續保持pending狀態,即該event可以無限次參加libevent的事件偵聽。
  156.             每當其被啟用觸發callback函式執行之後,該event自動從active轉回為pending狀態,
  157.             繼續參加libevent的偵聽(當啟用條件滿足,又可以繼續執行其callback)
  158.             除非在程式碼中使用event_del()函式將該event從libevent的偵聽事件集合中刪除。
  159.             如果不通過設定EV_PERSIST使得event是persistent的,需要在event的callback中再次呼叫event_add
  160.             (即在每次pending變為active之後,在callback中再將其設定為pending)
  161.      */
  162.     event_set(&ServerEvent, ServerSocket, EV_READ | EV_PERSIST, ServerAccept, NULL);
  163.     //將event新增到libevent偵聽的事件集中
  164.     if (event_add(&ServerEvent, 0) == -1) {
  165.         printf("event_add(): can not add accept event into libevent");
  166.         close(ServerSocket);
  167.         return -1;
  168.     }
  169.     return 0;
  170. }
  171. int main(int argc, char *argv[])
  172. {
  173.     int retval;
  174.     event_init(); //初始化event base使用預設的全域性current_base
  175.     retval = NewSocket();
  176.     if (retval == -1) {
  177.         exit(-1);
  178.     }
  179.     event_dispatch(); //啟動事件佇列系統,開始監聽(並接受)請求
  180.     return 0;
  181. }