/** * @file cf_socket.c * @author myusgun * @version 0.1 */ #include "cf_socket.h" #include "cf_local.h" #include "cf_error.h" #ifdef _WIN32 # pragma comment (lib, "ws2_32.lib") # define close(__sock) closesocket(__sock) # define sa_family_t unsigned short #else #endif #include #define CHECK_SOCKET_INIT() \ if (!CF_Socket_IsInitialized ()) \ return CF_ERROR_SOCKET_NOT_INITIALIZED #define CHECK_INVALID_SOCKET(__sock) \ if (__sock < 0) \ return CF_ERROR_SOCKET_INVALID_SOCKET static CF_BOOL gInitialized = CF_FALSE; /** * 소켓의 초기화 상태 확인 * * @return 초기화 된 경우, CF_TRUE; 그렇지 않은 경우, CF_FALSE */ CF_BOOL CF_Socket_IsInitialized (void) { return gInitialized; } /** * 소켓 초기화 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 */ int CF_Socket_Initialize (void) { int result = 0; #ifdef WIN32 WSADATA winsockData; result = WSAStartup (MAKEWORD (2, 0), &winsockData); #endif gInitialized = CF_TRUE; if (result != 0) return CF_ERROR_SOCKET_INITIALIZE; return CF_OK; } /** * 소켓 해제 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 */ int CF_Socket_Finalize (void) { int result = 0; CHECK_SOCKET_INIT (); #ifdef WIN32 result = WSACleanup (); #endif if (result != 0) return CF_ERROR_SOCKET_FINALIZE; return CF_OK; } /** * 소켓 닫기 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param sock 소켓 */ int CF_Socket_Close (const int sock) { int result = 0; CHECK_INVALID_SOCKET (sock); result = close (sock); if (result != 0) return CF_ERROR_SOCKET_CLOSE; return CF_OK; } /** * 소켓 옵션 설정 * * @return 성공 시 CF_OK; 실패 시, 오류 코드 * * @param sock 소켓 * @param optname 옵션 이름 * @param optval 설정할 옵션 값의 메모리 * @param optlen 설정할 옵션의 길이 */ int CF_Socket_SetOption (const int sock, const int optname, const void * optval, const size_t optlen) { int result = 0; CHECK_INVALID_SOCKET (sock); result = setsockopt (sock, SOL_SOCKET, optname, #ifdef _WIN32 (char *) optval, #else optval, #endif (socklen_t) optlen); if (result < 0) return CF_ERROR_SOCKET_SET_OPTION; return CF_OK; } /** * 소켓 옵션 얻기 * * @return 성공 시 CF_OK; 실패 시, 오류 코드 * * @param sock 소켓 * @param optname 옵션 이름 * @param optval 옵션 값을 가져올 메모리 * @param optlen 옵션 길이를 가져올 메모리 */ int CF_Socket_GetOption (const int sock, const int optname, void * optval, size_t * optlen) { int result = 0; CHECK_INVALID_SOCKET (sock); result = getsockopt (sock, SOL_SOCKET, optname, #ifdef _WIN32 (char *) optval, #else optval, #endif (socklen_t *) optlen); if (result < 0) return CF_ERROR_SOCKET_GET_OPTION; return CF_OK; } /** * 소켓에 타임아웃 설정 * * @return 성공 시 CF_OK; 실패 시, 오류 코드 * * @param sock 소켓 * @param timeout 타임아웃(sec) */ int CF_Socket_SetTimeout (const int sock, const int timeout) { int result = 0; #ifdef _WIN32 int time_ms = timeout * 1000; #else struct timeval timeval; timeval.tv_sec = timeout; timeval.tv_usec= 0; #endif CHECK_INVALID_SOCKET (sock); if (timeout < 0) return CF_ERROR_SOCKET_INVALID_ARGS; result = CF_Socket_SetOption (sock, SO_RCVTIMEO, #ifdef _WIN32 &time_ms, sizeof (time_ms)); #else &timeval, sizeof (timeval)); #endif if (result < 0) return CF_ERROR_SOCKET_SET_TIMEOUT; return CF_OK; } /** * 소켓 연결 * * @return 성공 시, 연결된 소켓; 실패 시, 오류 코드 * * @param ip 연결할 호스트의 주소 (도메인 이름 가능) * @param port 연결할 호스트의 포트번호 */ int CF_Socket_Connect (const char * ip, const unsigned short port) { int result = 0; int sock = 0; struct sockaddr_in address; struct hostent * hostEnt; CHECK_SOCKET_INIT (); sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) return CF_ERROR_SOCKET_CREATE; address.sin_family = AF_INET; address.sin_port = htons (port); address.sin_addr.s_addr = inet_addr (ip); TRY { if (address.sin_addr.s_addr == (unsigned int)-1) { hostEnt = gethostbyname (ip); if (hostEnt == NULL) { result = CF_ERROR_SOCKET_GET_HOST; TRY_BREAK; } address.sin_family = (sa_family_t) hostEnt->h_addrtype; memcpy (&(address.sin_addr.s_addr), hostEnt->h_addr, (size_t) hostEnt->h_length); } result = connect (sock, (struct sockaddr *) &address, sizeof (address)); if (result < 0) { result = CF_ERROR_SOCKET_CONNECT; TRY_BREAK; } } CATCH_IF (result < 0) { CF_Socket_Close (sock); return result; } return sock; } /** * 서버 열기 * * @return 성공 시, 서버 소켓; 실패 시, 오류 코드 * * @param port 서버 포트 * @param backlog listen 시의 backlog 수 */ int CF_Socket_Server (const unsigned short port, const int backlog) { int result = 0; int sock = 0; struct sockaddr_in address; CHECK_SOCKET_INIT (); sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) return CF_ERROR_SOCKET_CREATE; address.sin_family = AF_INET; address.sin_addr.s_addr = htonl (INADDR_ANY); address.sin_port = htons (port); TRY { result = bind (sock, (struct sockaddr *) &address, sizeof (struct sockaddr)); if (result < 0) { result = CF_ERROR_SOCKET_BIND; TRY_BREAK; } result = listen (sock, backlog); if (result < 0) { result = CF_ERROR_SOCKET_LISTEN; TRY_BREAK; } } CATCH_IF (result < 0) { CF_Socket_Close (sock); return result; } return sock; } /** * 소켓 연결 * * @return 성공 시, 연결된 소켓; 실패 시, 오류 코드 * * @param sock 서버 소켓 * @param address [옵션] 클라이언트 정보를 담을 sockaddr_in 구조체 포인터 */ int CF_Socket_Accept (const int sock, struct sockaddr_in * address) { int sockClient; struct sockaddr_in remoteAddress; socklen_t len = sizeof (remoteAddress); CHECK_INVALID_SOCKET (sock); sockClient = accept (sock, (struct sockaddr *) &remoteAddress, &len); if (sockClient < 0) return CF_ERROR_SOCKET_ACCEPT; if (address != NULL) memcpy ((void *) address, (void *) &remoteAddress, sizeof (struct sockaddr_in)); return sockClient; } /** * 데이터 송신 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param sock 소켓 * @param buf 송신할 데이터 * @param len 송신할 데이터의 길이 */ int CF_Socket_Send (const int sock, const void * buf, const int len) { int result = 0; CHECK_INVALID_SOCKET (sock); result = (int) send (sock, buf, (size_t) len, 0); if (result != len) return CF_ERROR_SOCKET_SEND; return CF_OK; } /** * 데이터 수신 * * @return 성공 시, 수신한 데이터의 길이; 실패 시, 오류 코드 * * @param sock 소켓 * @param buf 데이터를 수신할 버퍼 * @param len 데이터를 수신할 버퍼의 최대 크기 */ int CF_Socket_Recv (const int sock, void * buf, const int len) { int result = 0; CHECK_INVALID_SOCKET (sock); result = (int) recv (sock, buf, (size_t) len, 0); if (result < 0) return CF_ERROR_SOCKET_RECV; return result; }