libeventでechoサーバをつくってみた
memcachedで使われていることで有名なlibeventを試してみました。
以前libevを試したことがあるのですが、libeventの方が少し書きやすいという印象です。パフォーマンスに関してはlibevのほうが上という噂ですが。
libeventやlibevに関して少し説明しておくと、これらは非同期IOを実現するライブラリです。他にもシグナルやタイマー処理といったこともできるらしいです(まだ詳しく調べていません)。
非同期IOのAPIはOSごとに独自のもの(epoll, kqueueなど)があるのですが、libeventなどを利用するとその違いを隠蔽してくれるため、移植性が高まります。freebsdで開発してlinuxで動かすということだって出来ますね。
インストールに関しては、macの場合にはmacportsで簡単に入れられますし、linuxであればyumやapt-getで簡単に導入できます。
そしてechoサーバのコードです。エラー対策は全くしていません。
/* echoserver_libevent.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <event.h> #define PORT 1986 #define BACKLOG -1 #define BUFSIZE 256 void accept_handler(int listener, short event, void *arg); void recv_handler(int client, short event, void *arg); int main(int argc, const char* argv[]) { struct event ev; struct sockaddr_in sin; int listener; listener = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); memset(&sin, 0, sizeof(struct sockaddr_in)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = htons(PORT); bind(listener, (struct sockaddr*)&sin, sizeof(struct sockaddr)); listen(listener, BACKLOG); event_init(); event_set(&ev, listener, EV_READ | EV_PERSIST, accept_handler, &ev); event_add(&ev, NULL); event_dispatch(); return 0; } void accept_handler(int listener, short event, void *arg) { struct event *ev; struct sockaddr_in addr; int client; socklen_t addrlen = sizeof(addr); if (event & EV_READ) { client = accept(listener, (struct sockaddr*)&addr, &addrlen); ev = (struct event*)malloc(sizeof(struct event)); event_set(ev, client, EV_READ | EV_PERSIST, recv_handler, ev); event_add(ev, NULL); printf("connection accepted\n"); } } void recv_handler(int client, short event, void *arg) { char buf[BUFSIZE]; struct event *ev = (struct event*)arg; ssize_t recvlen; if (event & EV_READ) { if ((recvlen = recv(client, buf, BUFSIZE-1, 0)) <= 0) { event_del(ev); free(ev); close(client); printf("connection closed\n"); } else { send(client, buf, recvlen, 0); } } }
基本の流れは、
- struct eventを作って
- event_setでコールバックなどの設定を行って
- event_addで監視させ始める
となります。
重要な点は、
というところでしょうか。
コンパイルして動かして、telnetなり自作のクライアントなりでデータを送るとエコーバックします。
$ gcc echoserver_libevent.c -levent $ ./a.out $ telnet localhost 1986 aaa aaa test test ^] telnet> ^D