1. 程式人生 > >使用 libev 構建 TCP 響應伺服器(echo server)的簡單流程

使用 libev 構建 TCP 響應伺服器(echo server)的簡單流程

請注意這是 libev 而不是 libevent 的文章!
這篇文章主要是使用具體的例子,說明如何使用 libev。網上不少文章都是照抄示例,一點用都沒有……本文將示例的程式碼精簡一下,補上說明;大家都懂的部分就不贅述了。需要完整原始碼請檢視參考資料。

Reference

基本流程

  1. 建立 socket,繫結 socket 地址

  2. Listen socket

  3. 建立一個 watcher,用來承載accept事件

  4. 寫一個 callback 用來做實際的accept呼叫

  5. 建立並初始化一個 watcher 用來從 client 中讀取請求

  6. 寫一個 callback 用來read

  7. 啟動 event loop

建立 socket 並繫結 address

注意:原文例子中未顯示的是,應當將 fd 設定為非阻塞的。帶非阻塞設定的程式碼如下:

some_init_func()
{
    ...
    
    sd = socket (PF_INET, SOCK_STREAM, 0);
    flags = fcntl (sd, F_GETEL, 0);
    fcntl (sd, F_SETEL, flags | O_NONBLOCK);
    
    bzero (&addr, sizeof(addr));
    ...                    // 設定 Address
和 port bind (sd, (struct sockaddr *)(&addr), sizeof(addr)); ... }

監聽埠

some_init_func()
{
    ...
    
    listen (sd, 2);
    
    ...
}

準備用來accept()的 watcher

some_init_func()
{
    ...
    
    ev_io_init (&w_accept, accept_cb, sd, EV_READ);
    ev_io_start (loop, &w_accept);
    
    ...
}

回撥函式如下:

static void accept_cb (struct ev_loop *loop,
                       struct ev_io *watcher,
                       int revents)
{
    ...
    client_sd = accept (watcher->fd,                // accept() 呼叫,接受傳入連線
                        (struct sockaddr *)(&client_addr),
                        &client_len);
    ...
    w_client = (struct ev_io *)malloc(sizeof(struct ev_io));        // 為 read watcher 準備記憶體
    ...
    ev_io_init (w_client, read_cb, client_sd, EV_READ);             // 這裡就只示例 read 事件了。write 事件同理
    ev_io_start (loop, w_client);
}

準備用來read()的 callback

static void read_cb (struct ev_loop *loop,
                     struct ev_io *watcher,
                     int revents)
{
    ...
    readCount = recv (watcher->fd, buffer, BUFFER_SIZE, 0);        // 讀取的方法就視乎程式設計師的實現啦
    send (watcher->fd, buffer, readCount, 0);                      // 把資料 echo 回去
    ...
}

原文例子使用的就是recv/send,實際上我個人偏愛的是read/write

啟動 event loop

ev_loop (loop0); // 這裡可以直接使用 default loop

系列篇