/** @file tcp.c @date 2009-03-20 @author 오재경 freefrug@falinux.com @brief tcp 를 사용한 통신을 담당한다. @modify write() 함수 호출시 블럭되는 일이 발생하여 tcp_write_thread()함수 추가 send() 함수를 사용하여 블럭이 발생하지 않도록 tcp_write()함수를 수정하였으며 BF_POLICY_WRITE_NONBLOCK 옵션으로 사용한다. 2010-03-19 (오재경) poll_obj_t 구조체에서 on_disconnect 멤버 추가 소켓 접속이 끊어질 경우 on_disconnect 함수 호출 2010-08-18 (장길석) mingw와 함께 사용할 수 있는 코드 추가 @todo @bug @remark 쓰레드를 위해서 컴파일 옵션에 LDFLAGS += -lpthread 추가 @warning */ // // 저작권 에프에이리눅스(주) // 외부공개 금지 // //---------------------------------------------------------------------------- #define EMBEDDED_LINUX // 이렇게 처리하지 않으면 EClipse에서 C 영역이 회색 바탕이 됨 #ifdef MS_WIN32 #undef EMBEDDED_LINUX #endif #include #include #include #include #include #include #include #include #ifdef EMBEDDED_LINUX #include #include #include #include #include #include #include #include #include #include #else #include #include #endif #include #include #include #include char desc_tcp[] = "falinux tcp ver 0.2.1"; #ifdef EMBEDDED_LINUX // #define _USE_SEND_THREAD_ #endif /// tcp 개별 구조체 typedef struct { char host[256]; int port; int sock_type; struct sockaddr_in addr; // client 정보 poll_obj_t *obj_server; // 서버에서 분리된 클라이언트를 위해 long life_sec; // 입력이 특정시가나 동안 없으면 접속을 끊는다. // 서버용 정보 tlist *client_list; int client_max; #ifdef _USE_SEND_THREAD_ // 쓰레드 관련정보 pthread_mutex_t mutex_send; // 아래의 변수를 보호한다. int send_cnt; // 전송개수 int send_buf_len; // 전송버퍼 크기 char *send_buf; // 전송버퍼 #endif } tcp_priv_t; /// 변수 정의 static unsigned int tcp_live_sec = DEFAULT_LIVE_SEC; static unsigned int bf_policy = BF_POLICY_CLOSE_IOERR | BF_POLICY_READ_ZERO_ERROR; // | BF_POLICY_WRITE_NONBLOCK; #ifdef EMBEDDED_LINUX //------------------------------------------------------------------------------ /** @brief 소켓접속이 끊어진 소켓접속시 에러를 캡쳐 함수 @param signo 시그널 번호 *///---------------------------------------------------------------------------- static void sig_capture_SIGPIPE( int signo ) { // 시그널 캡쳐만하고 에러에 대한 처리는 하지 않는다. // printf("siganl BrokenPipe\n" ); signo = signo; // jwjw : warning을 피하기 위해 } //------------------------------------------------------------------------------ /** @brief broken pipe 캡쳐 설정 *///---------------------------------------------------------------------------- static int hook_sigpipe = 0; static void hook_SIGPIPE( void ) { struct sigaction sig; if ( 0 == hook_sigpipe ) { sig.sa_handler = sig_capture_SIGPIPE; sigemptyset(&sig.sa_mask); sig.sa_flags = 0; sigaction( SIGPIPE, &sig, 0 ); hook_sigpipe = 1; } } //------------------------------------------------------------------------------ /** @brief tcp 정책을 설정한다. @param option bit 에 옵션을 설정한다. @remark BF_POLICY_CLOSE_IOERR read(), write() 함수 에러발생시 접속을 끊는다. BF_POLICY_CLOSE_TIMEOUT SOCK_TYPE_SERVER_CLIENT 타입에 한해서 특정시간이 지난후라면 접속을 끊는다. BF_POLICY_RELOAD_TIMEOUT SOCK_TYPE_SERVER_CLIENT 타입에 한해서 시간을 재갱신한다. BF_POLICY_CLOSE_OFF_LINK SOCK_TYPE_SERVER_CLIENT 타입에 한해서 하드웨어적인 링크가 끊어지면 접속을 끊는다. BF_POLICY_READ_ZERO_ERROR read() 함수에서 읽은 값이 0이면 에러처리한다. *///---------------------------------------------------------------------------- void tcp_set_policy( unsigned int option ) { bf_policy = option; } //------------------------------------------------------------------------------ /** @brief tcp 정책을 얻는다. @return bf_policy *///---------------------------------------------------------------------------- unsigned int tcp_get_policy( void ) { return bf_policy; } //------------------------------------------------------------------------------ /** @brief 통신이 일정시간 없으면 접속을 끊는데 사용하는 시간을 설정한다. @param sec 생존시간 초단위 @remark SOCK_TYPE_SERVER_CLIENT 타입인 경우만 적용된다. *///---------------------------------------------------------------------------- void tcp_set_livesec( int sec ) { tcp_live_sec = sec; } #endif //------------------------------------------------------------------------------ /** @brief tcp 소켓을 서버형태로 open 한다. @param port 포트번호 @param max_client 접속할수 있는 클라이언트 최대개수 @return poll_obj_t 형태의 포인터 *///---------------------------------------------------------------------------- #ifdef EMBEDDED_LINUX poll_obj_t *tcp_open_server( int port, int max_client ) { struct sockaddr_in addr_in; poll_obj_t *obj; tcp_priv_t *tcp; int option; int sockfd; // broken pipe 캡쳐 설정 hook_SIGPIPE(); sockfd = socket( PF_INET, SOCK_STREAM, 0 ); if ( sockfd < 0 ) { perror( "tcp open error:" ); return NULL; } // TIME-WAIT 상태에 있는 소켓에 할당되어 있는 IP 주소와 포트를 바로 사용할 수 있도록 // SO_REUSEADDR 의 옵션 값을 TRUE 로 option = 1; setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); // 소켓을 시스템에 연결한다. bzero( &addr_in, sizeof(struct sockaddr_in) ); addr_in.sin_family = AF_INET; addr_in.sin_addr.s_addr = htonl( INADDR_ANY ); addr_in.sin_port = htons( port ); if( bind( sockfd, (struct sockaddr *)&addr_in, sizeof(struct sockaddr_in) ) < 0 ) { perror("tcp bind error :"); close( sockfd ); return NULL; } // listen if ( listen( sockfd, max_client ) < 0 ) { perror("tcp listen error :"); close( sockfd ); return NULL; } // tcp 만의 정보를 설정한다. tcp = (tcp_priv_t *)malloc( sizeof(tcp_priv_t) ); tcp->sock_type = SOCK_TYPE_SERVER; tcp->port = port; tcp->client_max = max_client; tcp->client_list = tlist_create(); #ifdef _USE_SEND_THREAD_ pthread_mutex_init( &tcp->mutex_send, NULL ); tcp->send_buf = NULL; tcp->send_buf_len = 0; tcp->send_cnt = 0; #endif // 폴관리객체에 등록한다. obj = poll_add( sockfd ); obj->type = STYP_TCP; obj->priv = (void *)tcp; //obj->on_poll_in = tcp_default_on_accept; poll_rebuild(); return obj; } #else poll_obj_t *tcp_open_server( int port, int max_client ) { SOCKET hSock; SOCKADDR_IN sckAddr; poll_obj_t *obj; tcp_priv_t *tcp; char option; hSock = socket( PF_INET, SOCK_STREAM, 0 ); if ( INVALID_SOCKET == hSock){ perror( "tcp server open error:"); return NULL; } hSock = socket( PF_INET, SOCK_STREAM, 0 ); if ( INVALID_SOCKET == hSock){ perror( "udp open error:"); return NULL; } // TIME-WAIT 상태에 있는 소켓에 할당되어 있는 IP 주소와 포트를 바로 사용할 수 있도록 // SO_REUSEADDR 의 옵션 값을 TRUE 로 option = 1; setsockopt( hSock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); // 소켓을 시스템에 연결한다. memset( &sckAddr, 0, sizeof( sckAddr)); sckAddr.sin_family = AF_INET; sckAddr.sin_addr.s_addr = htonl( INADDR_ANY ); sckAddr.sin_port = htons( port ); if( SOCKET_ERROR == bind( hSock, ( SOCKADDR *)&sckAddr, sizeof( sckAddr) )){ perror("tcp bind error"); closesocket( hSock ); return NULL; } // listen if ( SOCKET_ERROR == listen( hSock, max_client ) ){ perror("tcp listen error :"); closesocket( hSock ); return NULL; } // tcp 만의 정보를 설정한다. tcp = (tcp_priv_t *)malloc( sizeof(tcp_priv_t) ); tcp->sock_type = SOCK_TYPE_SERVER; tcp->port = port; tcp->client_max = max_client; tcp->client_list = tlist_create(); #ifdef _USE_SEND_THREAD_ pthread_mutex_init( &tcp->mutex_send, NULL ); tcp->send_buf = NULL; tcp->send_buf_len = 0; tcp->send_cnt = 0; #endif // 폴관리객체에 등록한다. fd_t pSock = malloc( sizeof( SOCKET)); *( SOCKET*)pSock = hSock; obj = poll_add( pSock ); obj->type = STYP_TCP; obj->priv = (void *)tcp; //obj->on_poll_in = tcp_default_on_accept; poll_rebuild(); return obj; } #endif //------------------------------------------------------------------------------ /** @brief tcp 소켓을 클라이언트 형태로 open 하고 상대편에 접속한다 @param ip IP문자열 포인터 @param port 포트번호 @return poll_obj_t 형태의 포인터 *///---------------------------------------------------------------------------- #ifdef EMBEDDED_LINUX poll_obj_t *tcp_open_client( char *ip, int port ) { struct sockaddr_in addr_in; poll_obj_t *obj; tcp_priv_t *tcp; int option; int sockfd; sockfd = socket( PF_INET, SOCK_STREAM, 0 ); if ( sockfd < 0 ) { perror( "tcp open error:" ); return NULL; } // TIME-WAIT 상태에 있는 소켓에 할당되어 있는 IP 주소와 포트를 바로 사용할 수 있도록 // SO_REUSEADDR 의 옵션 값을 TRUE 로 option = 1; setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); // 소켓을 상대편에 연결한다. bzero( &addr_in, sizeof(struct sockaddr_in) ); addr_in.sin_family = AF_INET; addr_in.sin_addr.s_addr = inet_addr( ip ); addr_in.sin_port = htons( port ); if ( 0 > connect( sockfd, (struct sockaddr *)&addr_in, sizeof(struct sockaddr_in) ) ) { perror( "tcp connect error :" ); close( sockfd ); return NULL; } // tcp 만의 정보를 설정한다. tcp = (tcp_priv_t *)malloc( sizeof(tcp_priv_t) ); tcp->sock_type = SOCK_TYPE_CLIENT; tcp->port = port; tcp->obj_server = NULL; tcp->life_sec = get_cur_sec() + tcp_live_sec; #ifdef _USE_SEND_THREAD_ pthread_mutex_init( &tcp->mutex_send, NULL ); tcp->send_buf = NULL; tcp->send_buf_len = 0; tcp->send_cnt = 0; #endif sprintf( tcp->host, "%s", ip ); memcpy ( &tcp->addr, &addr_in, sizeof(addr_in) ); // 폴관리객체에 등록한다. obj = poll_add( sockfd ); obj->type = STYP_TCP; obj->priv = (void *)tcp; return obj; } #else poll_obj_t *tcp_open_client( char *ip, int port ) { SOCKET hSock; SOCKADDR_IN sckAddr; poll_obj_t *obj; tcp_priv_t *tcp; char option; hSock = socket( PF_INET, SOCK_STREAM, 0 ); if ( INVALID_SOCKET == hSock){ printf( "tcp server open error:\n\r"); return NULL; } // 소켓을 시스템에 연결한다. memset( &sckAddr, 0, sizeof( sckAddr)); sckAddr.sin_family = AF_INET; sckAddr.sin_addr.s_addr = inet_addr( ip ); sckAddr.sin_port = htons( port ); // TIME-WAIT 상태에 있는 소켓에 할당되어 있는 IP 주소와 포트를 바로 사용할 수 있도록 // SO_REUSEADDR 의 옵션 값을 TRUE 로 option = 1; setsockopt( hSock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); // 서버에 연결을 시도한다. if ( SOCKET_ERROR == connect( hSock, ( SOCKADDR *)&sckAddr, sizeof( sckAddr) ) ){ printf( "tcp connect error :\n\r" ); closesocket( hSock ); return NULL; } // tcp 만의 정보를 설정한다. tcp = (tcp_priv_t *)malloc( sizeof(tcp_priv_t) ); tcp->sock_type = SOCK_TYPE_CLIENT; tcp->port = port; tcp->obj_server = NULL; tcp->life_sec = get_cur_sec() + tcp_live_sec; #ifdef _USE_SEND_THREAD_ pthread_mutex_init( &tcp->mutex_send, NULL ); tcp->send_buf = NULL; tcp->send_buf_len = 0; tcp->send_cnt = 0; #endif sprintf( tcp->host, "%s", ip ); memcpy ( &tcp->addr, &sckAddr, sizeof(sckAddr) ); // 폴관리객체에 등록한다. fd_t pSock = malloc( sizeof( SOCKET)); *( SOCKET*)pSock = hSock; obj = poll_add( pSock ); obj->type = STYP_TCP; obj->priv = (void *)tcp; return obj; } #endif //------------------------------------------------------------------------------ /** @brief 클라이언트 소켓의 접속을 폴관린 객체에 등록한다. @param obj_server 폴객체 포인터 @param sockfd 클라이언트 소켓 @param paddr 인터넷 어드레스구조체 포인터 @return poll_obj_t 형태의 포인터 *///---------------------------------------------------------------------------- #ifdef EMBEDDED_LINUX static poll_obj_t *tcp_connect_client( poll_obj_t *obj_server, int sockfd, struct sockaddr_in *paddr ) { poll_obj_t *obj; tcp_priv_t *tcp, *server_tcp; // 클라이언트 접속의 개수를 파악한다. server_tcp = (tcp_priv_t *)obj_server->priv; if ( tlist_getcount(server_tcp->client_list) >= server_tcp->client_max ) { close( sockfd ); printf( "tcp connect error : client is full (%d)\n", server_tcp->client_max ); return NULL; } // tcp 만의 정보를 설정한다. tcp = (tcp_priv_t *)malloc( sizeof(tcp_priv_t) ); tcp->sock_type = SOCK_TYPE_SERVER_CLIENT; tcp->port = -1; tcp->obj_server = obj_server; // 클라이언트 소켓을 할당한 서버정보 tcp->life_sec = get_cur_sec() + tcp_live_sec; #ifdef _USE_SEND_THREAD_ pthread_mutex_init( &tcp->mutex_send, NULL ); tcp->send_buf = NULL; tcp->send_buf_len = 0; tcp->send_cnt = 0; #endif sprintf( tcp->host, "%s", inet_ntoa( paddr->sin_addr) ); memcpy( &tcp->addr, paddr, sizeof(struct sockaddr_in) ); // 폴관리객체에 등록한다. obj = poll_add( sockfd ); if ( obj ) { // 서버에 접속된 클라이언트를 리스트에 등록 tlist_add( server_tcp->client_list, obj ); // 접속시간 관리가 필요하다면 if ( bf_policy & (BF_POLICY_CLOSE_TIMEOUT | BF_POLICY_CLOSE_OFF_LINK) ) { obj->on_timeout = tcp_check_time; } obj->type = STYP_TCP; obj->priv = (void *)tcp; return obj; } else { close( sockfd ); free( tcp ); return NULL; } } #else static poll_obj_t *tcp_connect_client( poll_obj_t *obj_server, SOCKET hSockClient, SOCKADDR_IN *sckAddr){ poll_obj_t *obj; tcp_priv_t *tcp, *server_tcp; fd_t pSock; // 클라이언트 접속의 개수를 파악한다. server_tcp = (tcp_priv_t *)obj_server->priv; if ( tlist_getcount(server_tcp->client_list) >= server_tcp->client_max ) { closesocket( hSockClient); printf( "tcp connect error : client is full (%d)\n", server_tcp->client_max ); return NULL; } // tcp 만의 정보를 설정한다. tcp = (tcp_priv_t *)malloc( sizeof(tcp_priv_t) ); tcp->sock_type = SOCK_TYPE_SERVER_CLIENT; tcp->port = -1; tcp->obj_server = obj_server; // 클라이언트 소켓을 할당한 서버정보 tcp->life_sec = get_cur_sec() + tcp_live_sec; #ifdef _USE_SEND_THREAD_ pthread_mutex_init( &tcp->mutex_send, NULL ); tcp->send_buf = NULL; tcp->send_buf_len = 0; tcp->send_cnt = 0; #endif sprintf( tcp->host, "%s", inet_ntoa( sckAddr->sin_addr) ); memcpy( &tcp->addr, sckAddr, sizeof(struct sockaddr_in) ); // 폴관리객체에 등록한다. pSock = malloc( sizeof( SOCKET)); *( SOCKET*)pSock = hSockClient; obj = poll_add( pSock ); if ( obj ){ // 서버에 접속된 클라이언트를 리스트에 등록 tlist_add( server_tcp->client_list, obj ); // 접속시간 관리가 필요하다면 if ( bf_policy & (BF_POLICY_CLOSE_TIMEOUT | BF_POLICY_CLOSE_OFF_LINK) ) { obj->on_timeout = tcp_check_time; } obj->type = STYP_TCP; obj->priv = (void *)tcp; return obj; } else { closesocket( hSockClient); free( tcp ); return NULL; } } #endif //------------------------------------------------------------------------------ /** @brief tcp 소켓을 close 한다. @param obj 폴객체 포인터 *///---------------------------------------------------------------------------- void tcp_close( poll_obj_t *obj ) { tcp_priv_t *tcp, *server_tcp; // 소켓 끊어짐을 사용자에게 알린다. if ( obj->on_disconnect ) { obj->on_disconnect( obj ); } tcp = (tcp_priv_t *)obj->priv; switch( tcp->sock_type ) { case SOCK_TYPE_SERVER_CLIENT : { // 서버가 관리하는 클라이언트를 리스트에서 삭제 server_tcp = (tcp_priv_t *)(tcp->obj_server->priv); if ( server_tcp ) { tlist_remove( server_tcp->client_list, obj ); } } break; case SOCK_TYPE_SERVER : { // 서버가 관리하는 클라이언트의 server_tcp 를 모두 NULL 로 만든다. // @@@@@@@@@@ tlist_free( tcp->client_list ); } break; } #ifdef EMBEDDED_LINUX close( obj->fd ); #else closesocket( *( ( SOCKET *)obj->fd)); free( obj->fd); // SOCKET * 를 해제한다. #endif if ( obj->priv ) { #ifdef _USE_SEND_THREAD_ if ( tcp->send_buf ) free( tcp->send_buf ); #endif free( obj->priv ); } poll_delete( obj ); } //------------------------------------------------------------------------------ /** @brief tcp 소켓의 접속요구를 처리한후 클라이언트소켓을 등록한다. @param obj_server tcp 서버 폴객체 포인터 @return tcp 클라이언트 폴객체 포인터 *///---------------------------------------------------------------------------- #ifdef EMBEDDED_LINUX poll_obj_t *tcp_accept_client( poll_obj_t *obj_server ) { struct sockaddr_in client_addr; int client; socklen_t addr_len; addr_len = sizeof(struct sockaddr_in); client = accept( obj_server->fd, (struct sockaddr *)&client_addr, &addr_len); return tcp_connect_client( obj_server, client, &client_addr ); } #else poll_obj_t *tcp_accept_client( poll_obj_t *obj_server ) { SOCKET hSockServer = *( SOCKET *)obj_server->fd; SOCKET hSockClient; SOCKADDR_IN sckAddr; int addr_len; addr_len = sizeof( sckAddr); hSockClient = accept( hSockServer, ( SOCKADDR *)&sckAddr, &addr_len); return tcp_connect_client( obj_server, hSockClient, &sckAddr ); } #endif //------------------------------------------------------------------------------ /** @brief tcp 소켓을 통해 데이타를 전송한다. @param obj 폴객체 포인터 @param buf 전송버퍼 @param len 버퍼의 길이 @return 전송한 데이타 개수 @remark 에러에 대한 처리를 해야한다. tcp_close( obj ) 를 호출하여 접속을 끊는 방법이 일반적이다. BF_POLICY_AUTO_CLOSE 옵션이 있을경우 자동으로 소켓을 닫는다. *///---------------------------------------------------------------------------- int tcp_write( poll_obj_t *obj, char *buf, int len ) { tcp_priv_t *tcp; int wrcnt; #ifdef EMBEDDED_LINUX // NON 블럭 옵션이 있다면 if ( bf_policy & BF_POLICY_WRITE_NONBLOCK ) { wrcnt = send( obj->fd, buf, len, MSG_DONTWAIT); } else { wrcnt = write( obj->fd, buf, len ); } #else SOCKET hSock = *(( SOCKET *)obj->fd); wrcnt = send( hSock, buf, len, 0); #endif if ( 0 > wrcnt ){ perror( "tcp send error:" ); if ( bf_policy & BF_POLICY_CLOSE_IOERR ) { tcp_close( obj ); } } else { // 정책에 대한 결정을 한다. tcp = (tcp_priv_t *)obj->priv; if ( tcp->sock_type == SOCK_TYPE_SERVER_CLIENT ) { if ( bf_policy & BF_POLICY_RELOAD_TIMEOUT ) { tcp->life_sec = get_cur_sec() + tcp_live_sec; } } } return wrcnt; } #ifdef _USE_SEND_THREAD_ //------------------------------------------------------------------------------ /** @brief 전송 쓰레드함수이다. @param obj 폴객체 포인터 @return *///---------------------------------------------------------------------------- static void *thread_write_func( poll_obj_t *obj ) { tcp_priv_t *tcp; int wrcnt; tcp = (tcp_priv_t *)obj->priv; wrcnt = 0; if ( tcp->send_buf && ( 0 < tcp->send_cnt ) ) { wrcnt = write( obj->fd, tcp->send_buf, tcp->send_cnt ); } pthread_mutex_lock( &tcp->mutex_send ); tcp->send_cnt = 0; pthread_mutex_unlock( &tcp->mutex_send ); return (void *)wrcnt; } //------------------------------------------------------------------------------ /** @brief 쓰레드를 생성하여 데이타를 전송한다. @param obj 폴객체 포인터 @param buf 전송버퍼 @param len 버퍼의 길이 @return 전송한 데이타 개수 *///---------------------------------------------------------------------------- int tcp_write_thread( poll_obj_t *obj, char *buf, int len ) { obj = obj; // jwjw: warning을 피하기 위해 buf = buf; // jwjw: warning을 피하기 위해 len = len; // jwjw: warning을 피하기 위해 #ifdef _USE_SEND_THREAD_ tcp_priv_t *tcp; int rtn = 0; tcp = (tcp_priv_t *)obj->priv; pthread_mutex_lock( &tcp->mutex_send ); // 전송중인 쓰레드가 살아 있는가? if ( 0 == tcp->send_cnt ) { // 버퍼의 크기가 작다면 재 할당한다. if ( len > tcp->send_buf_len ) { if ( tcp->send_buf ) free( tcp->send_buf ); tcp->send_buf_len = ((len + 4096-1)/4096)*4096; tcp->send_buf = malloc( tcp->send_buf_len ); } tcp->send_cnt = len; memcpy( tcp->send_buf, buf, tcp->send_cnt ); // 쓰레드 생성 { pthread_t thr_id; pthread_attr_t thr_attributes; int thr_status; // 쓰레드 생성시 프로세스와 분리 pthread_attr_init( &thr_attributes ); pthread_attr_setdetachstate( &thr_attributes, PTHREAD_CREATE_DETACHED ); // 쓰레드 생성 thr_status = pthread_create( &thr_id, &thr_attributes, thread_write_func, (void *)obj ); if ( thr_status != 0 ) { perror( "pthread_create() " ); rtn = -1; } else { rtn = len; } } } else { rtn = 0; } pthread_mutex_unlock( &tcp->mutex_send ); return rtn; #else printf( "not surported %s()\n", __FUNCTION__ ); return -1; #endif } #endif //------------------------------------------------------------------------------ /** @brief tcp 소켓을 통해 데이타를 읽는다. @param obj 폴객체 포인터 @param len 버퍼의 길이 @return 전송된 데이타 개수 @remark 에러에 대한 처리를 해야한다. tcp_close( obj ) 를 호출하여 접속을 끊는 방법이 일반적이다. BF_POLICY_AUTO_CLOSE 옵션이 있을경우 자동으로 소켓을 닫는다. *///---------------------------------------------------------------------------- int tcp_read( poll_obj_t *obj, char *buf, int len ) { tcp_priv_t *tcp; int rdcnt; #ifdef EMBEDDED_LINUX rdcnt = read( obj->fd, buf, len ); #else SOCKET hSock = *(( SOCKET *)obj->fd); rdcnt = recv( hSock, buf, len, 0 ); #endif if ( 0 > rdcnt ) { perror( "tcp recv error:" ); if ( bf_policy & BF_POLICY_CLOSE_IOERR ) { tcp_close( obj ); } } else if ( 0 == rdcnt ) { if ( 0 > errno ) { perror( "tcp recv error:" ); if ( bf_policy & BF_POLICY_CLOSE_IOERR ) { tcp_close( obj ); } } else { if ( bf_policy & BF_POLICY_READ_ZERO_ERROR ) { printf( "tcp read 0 error: close socket\n" ); tcp_close( obj ); } } } // 정책에 대한 결정을 한다. tcp = (tcp_priv_t *)obj->priv; if ( tcp->sock_type == SOCK_TYPE_SERVER_CLIENT ) { if ( bf_policy & BF_POLICY_RELOAD_TIMEOUT ) { tcp->life_sec = get_cur_sec() + tcp_live_sec; } } return rdcnt; } //------------------------------------------------------------------------------ /** @brief 일정시간이 지난을때 호출되며 tcp 정책에 의해 일을 수행한다. @remark BF_POLICY_CLOSE_TIMEOUT, BF_POLICY_CLOSE_OFF_LINK 옵션을 사용한다면 이함수가 자동 등록되어 수행된다. link-off 처리는 되지 않는다. @return 리턴값은 의미없다 *///---------------------------------------------------------------------------- int tcp_check_time( poll_obj_t *obj ) { tcp_priv_t *tcp; // 타임오버 처리 if ( bf_policy & BF_POLICY_CLOSE_TIMEOUT ) { tcp = (tcp_priv_t *)obj->priv; if ( ( unsigned int)tcp->life_sec < get_cur_sec() ) { printf( "tcp close by timeout\n" ); tcp_close( obj ); } } // 링크 off 처리 if ( bf_policy & BF_POLICY_CLOSE_OFF_LINK ) { // } return 0; } //------------------------------------------------------------------------------ /** @brief tcp 서버에서 접속된 소켓의 개수를 얻는다. @param obj_server tcp 서버 폴객체 포인터 @return 접속된 클라이언트 개수 *///---------------------------------------------------------------------------- int tcp_client_count( poll_obj_t *obj_server ) { tcp_priv_t *tcp; tcp = (tcp_priv_t *)obj_server->priv; if ( tcp->client_list ) { return tlist_getcount( tcp->client_list ); } return 0; } //------------------------------------------------------------------------------ /** @brief tcp 서버에서 인덱스로 접속된 소켓 폴 객체를 얻는다. @param obj_server tcp 서버 폴객체 포인터 @param idx 인덱스 @return 클라이언트 폴객체 *///---------------------------------------------------------------------------- poll_obj_t *tcp_get_client( poll_obj_t *obj_server, int idx ) { tcp_priv_t *tcp; tcp = (tcp_priv_t *)obj_server->priv; if ( tcp->client_list && ( 0 <= idx ) ) { if ( idx < tlist_getcount( tcp->client_list ) ) { return tlist_get( tcp->client_list, idx ); } } return NULL; } //------------------------------------------------------------------------------ /** @brief tcp 클라이언트 폴객체에서 서버객체를 얻는다. @param obj_client tcp 클라이언트 폴객체 포인터 @return 서버 폴객체 *///---------------------------------------------------------------------------- poll_obj_t *tcp_get_server( poll_obj_t *obj_client ) { tcp_priv_t *tcp; tcp = (tcp_priv_t *)obj_client->priv; return tcp->obj_server; } //------------------------------------------------------------------------------ /** @brief 연결된 상대편의 IP 문자열을 얻는다. @param obj_client tcp 클라이언트 폴객체 포인터 @return 연결된 ip를 문자열로 돌려준다. 없으면 NULL *///---------------------------------------------------------------------------- const char *tcp_peer_ip_string( poll_obj_t *obj_client ) { tcp_priv_t *tcp; tcp = (tcp_priv_t *)obj_client->priv; if ( tcp ) { return tcp->host; } return NULL; }