epoll + 使い方

libevent のソースコードを参考にしつつ epoll を使ってみたメモ。
ただの興味本位。
動作は、Linux CentOS 5 で確認。
エラー処理とかはやってたりやってなかったり、わりと適当。

server.cpp

/**  
 * @file    server.cpp  
 * @brief   epoll test server.  
 */  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <stdarg.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <sys/types.h>  
#include <sys/epoll.h>  
  
#define LISTEN_PORT (12345) ///< 待ち受けポート  
#define MAX_EVENTS (1000) ///< 最大イベント数  
#define MAX_BACKLOG (10) ///< リクエスト待機キューの格納数  
  
struct epollev  
{  
    void (*callback)(int, void *); ///< callback function.  
    void *arg; ///< user data.  
};  
  
struct epollop  
{  
    int epfd; ///< epoll fd.  
    struct epoll_event *events; ///< epoll events.  
    int num_events; ///< maximum number of events.  
    struct epollev *fds; ///< fds  
};  
static epollop g_epollop;  
  
static void callback_accept( int fd, void *arg );  
static void callback_client( int fd, void *arg );  
  
//! @brief    強制終了  
static void server_exit( const char *log )  
{  
    perror( log );  
    exit( EXIT_FAILURE );  
}  
  
//! @brief    イベント初期化  
static void server_init()  
{  
    int epfd, nfiles = MAX_EVENTS;  
  
    if( ( epfd = epoll_create( nfiles ) ) == -1 ){  
        server_exit( "epoll_create()" );  
    }  
    g_epollop.epfd = epfd;  
    g_epollop.events = new struct epoll_event[nfiles];  
    memset( g_epollop.events, 0, sizeof(struct epoll_event)*nfiles );  
    g_epollop.num_events = MAX_EVENTS;  
    g_epollop.fds = new epollev[nfiles];  
    memset( g_epollop.fds, 0, sizeof(struct epollev)*nfiles );  
}  
  
//! @brief    イベント追加  
static void server_add( int fd, int events, void (*callback)(int, void *), void *arg )  
{  
    struct epoll_event epev = {0, {0}};  
    struct epollev *ev = &g_epollop.fds[fd];  
  
    int op = EPOLL_CTL_ADD;  
    if( ev->callback != NULL ){  
        op = EPOLL_CTL_MOD;  
    }  
  
    epev.data.fd = fd;  
    epev.events = events;  
    if( epoll_ctl( g_epollop.epfd, op, fd, &epev ) == -1 ){  
        perror( "epoll_ctl()" );  
        return;  
    }  
    ev->callback = callback;  
    ev->arg = arg;  
}  
  
//! @brief    イベント削除  
static void server_del( int fd )  
{  
    struct epoll_event epev = {0, {0}};  
    struct epollev *ev = &g_epollop.fds[fd];  
    int op;  
  
    op = EPOLL_CTL_DEL;  
    epev.data.fd = fd;  
    memset( ev, 0, sizeof( struct epollev ) );  
  
    if( epoll_ctl( g_epollop.epfd, op, fd, &epev ) == -1 ){  
        perror( "epoll_ctl()" );  
        return;  
    }  
    close( fd );  
}  
  
//! @brief    受付  
static void server_listen()  
{  
    int sock;  
    struct sockaddr_in saddr;  
  
    if( ( sock = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ){  
        server_exit( "socket()" );  
    }  
  
    memset( &saddr, 0, sizeof( saddr ) );  
    saddr.sin_family    = AF_INET;  
    saddr.sin_addr.s_addr = htonl( INADDR_ANY );  
    saddr.sin_port = htons( LISTEN_PORT );  
  
    if( bind( sock, (struct sockaddr *) &saddr, sizeof(saddr) ) != 0 ){  
        close( sock );  
        server_exit( "bind()" );  
    }  
  
    if( listen( sock, MAX_BACKLOG ) < 0 ){  
        close( sock );  
        server_exit( "listen()" );  
    }  
  
    server_add( sock, EPOLLIN, callback_accept, NULL );  
}  
  
//! @brief    メインループ  
static int server_loop( )  
{  
    fprintf( stdout, "in server_loop.\n" );  
  
    while( true ){  
        int i, res;  
        int timeout = -1;  
        struct epoll_event *events = g_epollop.events;  
  
        res = epoll_wait( g_epollop.epfd, events, g_epollop.num_events, timeout );  
        if( res == -1 ){  
            server_exit( "epoll_wait()" );  
        }  
  
        for( i = 0; i < res; ++i ){  
            int fd = events[i].data.fd;  
            struct epollev *ev = &g_epollop.fds[fd];  
  
            ev->callback( fd, ev->arg );  
        }  
    }  
    return 0;  
}  
  
//! @brief    受付コールバック  
static void callback_accept( int fd, void *arg )  
{  
    fprintf( stdout, "in callback_accept. fd:%d\n", fd );  
  
    int sock;  
    struct sockaddr saddr;  
    socklen_t len = sizeof( struct sockaddr_in );  
  
    if( ( sock = accept( fd, (struct sockaddr *)&saddr, &len ) ) < 0 ){  
        perror( "accept()" );  
        return;  
    }  
  
    int flag = fcntl( sock, F_GETFL, 0 );  
    fcntl( sock, F_SETFL, flag | O_NONBLOCK );  
  
    fprintf( stdout, "accept. sock:%d\n", sock );  
    server_add( sock, EPOLLIN, callback_client, NULL );  
}  
  
//! @brief    クライアントコールバック  
static void callback_client( int fd, void *arg )  
{  
    fprintf( stdout, "in callback_client. fd:%d\n", fd );  
  
    char buff[1024];  
    int ret = ::recv( fd, buff, sizeof(buff), 0 );  
    if( ret <= 0 ){  
        fprintf( stdout, "exit. fd:%d ret:%d\n", fd, ret );  
        server_del( fd );  
    }  
    else{  
        fprintf( stdout, "fd:%d -> %s\n", fd, buff );  
    }  
}  
  
int main()  
{  
    server_init();  
    server_listen();  
    return server_loop();  
}  

client.cpp

/**  
 * @file    client.cpp  
 * @brief   test client.  
 */  
#include <stdio.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <sys/time.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <netdb.h>  
#include <unistd.h>  
#include <fcntl.h>  
  
#define CONNECT_ADDR "127.0.0.1"  
#define CONNECT_PORT (12345)  
  
bool setnonblock(int fd)  
{  
    int flags = fcntl( fd, F_GETFL );  
    if( flags < 0 ){  
        return false;  
    }  
  
    if( fcntl( fd, F_SETFL, flags |= O_NONBLOCK ) < 0 ){  
        return false;  
    }  
    return true;  
}  
  
int main(void)  
{  
    int ret = 0, sock = 0, clilen = 0;  
    struct sockaddr_in cliaddr;  
  
    // socket  
    if( ( sock = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ){  
        puts( "socket error!!" );  
        return 1;  
    }  
  
    memset( &cliaddr, 0, sizeof( cliaddr ) );  
    cliaddr.sin_addr.s_addr = inet_addr( CONNECT_ADDR );  
    cliaddr.sin_family    = AF_INET;  
    cliaddr.sin_port    = htons( CONNECT_PORT );  
  
    clilen = sizeof( cliaddr );  
    if( ( ret = connect( sock, (struct sockaddr *)&cliaddr, clilen ) ) < 0 ){  
        perror( "connect()" );  
        return 1;  
    }  
    if( !setnonblock( sock ) ){  
        perror( "setnonblock()" );  
    }  
  
    puts( "in client loop." );  
    char str[1024];  
    char *strpos;  
    while( 1 )  
    {  
        memset( str, 0, sizeof( str ) );  
        fgets( str, sizeof(str), stdin );  
  
        // \n を除去  
        if( ( strpos = strchr( str, '\n' ) ) != NULL ){  
            (*strpos) = '\0';  
        }  
  
        // exit  
        if( strncmp( str, "exit", 4 ) == 0 )  
            break;  
  
        // send  
        if( send( sock, str, strlen( str ) + 1, 0 ) < 0 ){  
            perror( "send()" );  
            break;  
        }  
    }  
  
    // 後始末  
    while( 1 ){  
        ret = recv( sock, str, sizeof(str), 0 );  
        if( ret <= 0 )  
            break;  
    }  
    close( sock );  
  
    return 0;  
}