epoll を使った echo サーバ

自分用メモ。epoll & Non-Blocking IO による echo サーバの例。
使い方のテスト以上のものではないので、そのままではいろいろまずい。epoll_create や socket で取得した file descriptor を close してなかったり。まねしないように。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <netinet/in.h>

#define SERVER_PORT 10007
#define MAX_EVENTS 10
#define BACKLOG 10

static int listener;
static int epfd;

static void die(const char* msg)
{
    perror(msg);
    exit(EXIT_FAILURE);
}

static void setnonblocking(int sock)
{
    int flag = fcntl(sock, F_GETFL, 0);
    fcntl(sock, F_SETFL, flag | O_NONBLOCK);
}

static 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, BACKLOG) < 0) {
        close(sock);
        die("listen");
    }

    return sock;
}

int main()
{
    struct epoll_event ev;
    struct epoll_event events[MAX_EVENTS];
    char buffer[1024];

    if ((epfd = epoll_create(MAX_EVENTS)) < 0) {
        die("epoll_create");
    }

    listener = setup_socket();

    memset(&ev, 0, sizeof ev);
    ev.events = EPOLLIN;
    ev.data.fd = listener;
    epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev);

    for (;;) {
        int i;
        int nfd = epoll_wait(epfd, events, MAX_EVENTS, -1);

        for (i = 0; i < nfd; i++) {
            if (events[i].data.fd == listener) {
                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) {
                    perror("accept");
                    continue;
                }

                setnonblocking(client);
		memset(&ev, 0, sizeof ev);
                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = client;
                epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev);
            } else {
                int client = events[i].data.fd;
                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);
                }
            }
        }
    }

    return 0;
}