libevでechoサーバ
- libev版 echoサーバ
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <ev.h> #define SERVER_PORT 8011 #define MAX_BACKLOG 10 #define MAX_EVENTS 100 #define RCVBUFSIZE 256 // 異常終了 void die(const char* msg) { perror(msg); exit(EXIT_FAILURE); } // ノンブロッキングI/Oに設定する void setnonblocking(int sock) { int flag = fcntl(sock, F_GETFL, 0); fcntl(sock, F_SETFL, flag | O_NONBLOCK); } // ECHOサーバの設定 int setup_socket() { int sock; struct sockaddr_in sin; if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { die("socket"); } memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = htons(SERVER_PORT); if (bind(sock, (struct sockaddr *) &sin, sizeof sin) < 0) { close(sock); die("bind"); } if (listen(sock, MAX_BACKLOG) < 0) { close(sock); die("listen"); } return sock; } // クライアントからのイベントを処理する void event_client(EV_P_ struct ev_io *w, int revents) { char buf[RCVBUFSIZE + 1]; size_t n = recv(w->fd, buf, RCVBUFSIZE, 0); if (n < 0) { perror("recv"); } if (n <= 0) { close(w->fd); ev_io_stop(EV_A_ w); free(w); } else { buf[n] = '\0'; send(w->fd, buf, n, 0); } } // サーバへの接続要求イベントを処理する void event_server(EV_P_ struct ev_io *w, int revents) { struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); struct ev_loop *l; ev_io *client_watcher; int client = accept(w->fd, (struct sockaddr *) &client_addr, &client_addr_len); if (client < 0) { if (EINTR == errno) { return; } die("accept"); } setnonblocking(client); client_watcher = calloc(1, sizeof(ev_io)); l = w->data; // ev_ioの初期化と開始(クライアントのイベントを監視) ev_io_init(client_watcher, event_client, client, EV_READ); ev_io_start(l, client_watcher); } // メイン関数 int main() { struct ev_loop *loop; ev_io watcher; // サーバ起動 int listener = setup_socket(); // イベントループの初期化 loop = ev_default_loop(0); watcher.data = loop; // ev_ioの初期化と開始(サーバへの接続要求を監視) ev_io_init(&watcher, event_server, listener, EV_READ); ev_io_start(loop, &watcher); // イベントループ開始 ev_loop(loop, 0); close(listener); return 0; }
ビルドするには、libevをインストールしておく必要があります。libeioのソースコードは、Node.jsのdeps配下にあります。
※ちなみにNode.jsは、deps配下にあるライブラリをstaticリンクするため、libev等を事前にインストールする必要はありません。
- libevをインストール /usr/local/libにインストールされます。
$ cd libev $ ./configure $ make $ make install
- 共有ライブラリのパスに「/usr/local/lib」を追加する。
$ sudo vi /etc/ld.so.conf include /etc/ld.so.conf.d/*.conf # 1行追加 /usr/local/lib $ sudo ldconfig
libevを使用する場合は、無限ループを作成する必要がありません。ev_loop()で、イベントループとよばれる、イベントを監視するループが実行されます。イベントループに関数を設定して、イベントを監視します。