#include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <time.h> #include <err.h> #include <fcntl.h> #include <signal.h> #include <event.h> #define SET_PERSIST // 設定時にイベント継続を指定する場合、定義します。 #define ADD_TIME_OUT // 追加時に timeval を指定する場合、定義します。 // タイマー struct timeval *g_ptimeout = NULL; const int PORT_NUM = 2345; // 2345番ポート //--------------------------------------------------------------------------- // シグナル終了合図 //--------------------------------------------------------------------------- void sigTerm( int pid ) { delete g_ptimeout; exit( 1 ); } //--------------------------------------------------------------------------- // イベント削除 //--------------------------------------------------------------------------- void deleteEvent( int fd, struct event *pev ) { // ソケットディスクリプタを閉じる close( fd ); // イベント削除 event_del( pev ); // メモリ解放 delete pev; } //--------------------------------------------------------------------------- // 受信処理 // @param fd ファイルディスクリプタ // @param event イベントフラグ // @param *arg なんでも領域 //--------------------------------------------------------------------------- void connection_read( int fd, short event, void *arg ) { // デバッグ表示 printf( "%s(): fd:%d, event:%d.\n", __func__, fd, event); struct event *pev = (struct event *)arg; if( event & EV_READ ) { //--------------------------------------------- // 受信イベント //--------------------------------------------- char chatLog[255]; char str[255]; memset( chatLog, 0, sizeof( chatLog ) ); memset( str, 0, sizeof( str ) ); // データ受信 int ret = recv( fd, chatLog, 255, 0 ); if( ret == 0 ) { // 終了 puts( "ret == 0. shutdown." ); deleteEvent( fd, pev ); } else if( ret < 0 ) { // エラー if( errno != EWOULDBLOCK ) { printf( "ret:%d. error.\n", ret ); deleteEvent( fd, pev ); } else { // 読み込み未完了 printf( "ret:%d. would_block\n", ret ); } } else { // 受信データをそのまま表示 printf( "ret:%d. chat_log:%s\n", ret, chatLog ); // クライアントにエコー char echoStr[1024]; sprintf( echoStr, "echo -> %s", chatLog ); if( send( fd, echoStr, strlen( echoStr ) + 1, 0 ) < 0 ) { printf( "send error!!\n" ); deleteEvent( fd, pev ); } #ifndef SET_PERSIST //--------------------------------------------- // EV_PERSIST フラグが設定されていない場合、 // 再度 event_add を行う必要があります。 //--------------------------------------------- event_add( pev, g_ptimeout ); #else #ifdef ADD_TIME_OUT //--------------------------------------------- // timeval が設定されている場合、 // 再度 event_add を行う必要があります。 //--------------------------------------------- event_add( pev, g_ptimeout ); #endif #endif } } else if( event & EV_TIMEOUT ) { //--------------------------------------------- // タイムアウトイベント // このときイベントはすでに削除されています。 //--------------------------------------------- printf( "Time Out. fd:%d\n", fd ); close( fd ); delete pev; } } //--------------------------------------------------------------------------- // 接続 // @param fd ファイルディスクリプタ // @param event イベントフラグ // @param *arg なんでも領域 //--------------------------------------------------------------------------- void connection_accept( int fd, short event, void *arg ) { // デバッグ表示 fprintf( stderr, "%s(): fd = %d, event = %d.\n", __func__, fd, event ); if( event & EV_READ ) { //--------------------------------------------- // 受信イベント //--------------------------------------------- struct sockaddr_in sock_in; socklen_t len = sizeof( sock_in ); //--------------------------------------------- // accept //--------------------------------------------- int newsock = accept( fd, (struct sockaddr *) &sock_in, &len ); if( newsock < 0 ) { perror( "accept" ); return; } //--------------------------------------------- // 接続してきたクライアント用のイベントを設定 //--------------------------------------------- struct event *pev = new struct event; #ifdef SET_PERSIST //--------------------------------------------- // libevent 設定 // EV_READ に加えて EV_PERSIST を設定することで、 // イベントが内部で削除されるのを防ぎます。 //--------------------------------------------- event_set( pev, newsock, EV_READ | EV_PERSIST, connection_read, pev ); #else //--------------------------------------------- // libevent 設定 // EV_READ のみを設定すると、EV_READイベントを // 一度受け取った際、内部でイベントが削除されます。 //--------------------------------------------- event_set( pev, newsock, EV_READ, connection_read, pev ); #endif #ifdef ADD_TIME_OUT if( !g_ptimeout ) { g_ptimeout = new struct timeval; g_ptimeout->tv_sec = 10; // 10秒でタイムアウトイベント発生するように設定 g_ptimeout->tv_usec = 0; } #endif //--------------------------------------------- // 先ほど設定したイベントを追加 // NULL 以外を追加すると、EV_PERSIST 指定は無効になります。 //--------------------------------------------- event_add( pev, g_ptimeout ); } else if( event & EV_TIMEOUT ) { //--------------------------------------------- // タイムアウトイベント // acceptでは来ない予定。 //--------------------------------------------- printf( "Time Out. fd:%d\n", fd ); } } //--------------------------------------------------------------------------- // メイン // CFLAGS=-DUSE_DEBUG(./configure 時指定で libevent のデバッグ) //--------------------------------------------------------------------------- int main() { //--------------------------------------------- // ソケット作成 //--------------------------------------------- int sock = socket( PF_INET, SOCK_STREAM, 0 ); if( sock < 0 ) { perror( "socket" ); exit(1); } //--------------------------------------------- // bind //--------------------------------------------- struct sockaddr_in sock_in; bzero( &sock_in, sizeof( sock_in ) ); sock_in.sin_family = AF_INET; sock_in.sin_port = htons( PORT_NUM ); sock_in.sin_addr.s_addr = INADDR_ANY; if( bind( sock, (struct sockaddr *) &sock_in, sizeof( sock_in ) ) < 0 ) { perror("bind"); exit(1); } //--------------------------------------------- // listen //--------------------------------------------- if( listen( sock, 128 ) < 0 ) { perror( "listen" ); exit(1); } //--------------------------------------------- // libevent を初期化 //--------------------------------------------- event_init(); struct event ev; //--------------------------------------------- // libevent 設定 // EV_READ に加えて EV_PERSIST を設定することで、 // イベントが内部で削除されるのを防ぎます。 // accept は何度も繰り返しイベントが発生するので、EV_PRESIST 指定 //--------------------------------------------- event_set( &ev, sock, EV_READ | EV_PERSIST, connection_accept, &ev ); //--------------------------------------------- // libevent 追加 // NULL 以外を追加すると、EV_PERSISTは無効になります。 // accept は何度も繰り返しイベントが発生するので、NULL //--------------------------------------------- event_add( &ev, g_ptimeout ); // method check. printf( "event_method : %s\n", event_get_method() ); puts( "starting..." ); //--------------------------------------------- // イベント開始 //--------------------------------------------- event_dispatch(); puts( "finish." ); delete g_ptimeout; return 0; }