epollでechoサーバ
epollとlibevをそれぞれ使って、echoサーバを実装すると以下のようになります。
ubuntu 10.10で実行しました。
- epoll版 echoサーバ
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/epoll.h> #include <unistd.h> #include <fcntl.h> #include <netinet/in.h> #define SERVER_PORT 8012 #define MAX_BACKLOG 10 #define MAX_EVENTS 100 // 異常終了 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(int epfd, int client, struct epoll_event ev) { char buffer[1024]; int n = read(client, buffer, sizeof buffer); if (n < 0) { perror("read"); epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev); close(client); } else if (n == 0) { epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev); close(client); } else { write(client, buffer, n); } } // サーバへの接続要求イベントを処理する void event_server(int epfd, int listener, struct epoll_event ev) { struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof client_addr; int client = accept(listener, (struct sockaddr *) &client_addr, &client_addr_len); if (client < 0) { die("accept"); } setnonblocking(client); memset(&ev, 0, sizeof ev); ev.events = EPOLLIN | EPOLLET; ev.data.fd = client; epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev); } // メイン関数 int main() { int listener, epfd; struct epoll_event ev; struct epoll_event events[MAX_EVENTS]; // サーバ起動 listener = setup_socket(); // epollの初期化 if ((epfd = epoll_create(MAX_EVENTS)) < 0) { die("epoll_create"); } memset(&ev, 0, sizeof ev); ev.events = EPOLLIN; ev.data.fd = listener; epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev); // 無限ループ while (1) { int i; // 接続があるまで待つ int nfd = epoll_wait(epfd, events, MAX_EVENTS, -1); // 接続されているクライアント数分、処理を行う for (i = 0; i < nfd; i++) { // 新規接続の場合 if (events[i].data.fd == listener) { event_server(epfd, listener, ev); } else { event_client(epfd, events[i].data.fd, ev); } } } return 0; }
epoll()を使う場合、自前で無限ループをつくり、その中で、epoll_wait()を呼び出し、クライアントからの接続を待つ必要があります。epoll_wait()の最後のパラメータにマイナスの値を設定すると、タイムアウトせずにひたすら待ちます。