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; }