#include "BaseSocket.h" #include #include #include #include #include #include #include #include #include #include #define SOCKET_NOT_VALID -1 #define MAX_SEND_RETRY_COUNT 10 // Socket send ½ÇÆÐ½Ã ÃÖ´ë ÀçÀü¼Û ½Ãµµ Ƚ¼ö. /// @brief »ý¼ºÀÚ. /// @param socket [in] ó¸®ÇÒ socket descriptor CBaseSocket::CBaseSocket( const int& socket ) : m_sock( socket ) { if( m_sock < 0 ) { m_sock = SOCKET_NOT_VALID; m_bConnected = false; } else { m_bConnected = true; } } /// @brief ¼Ò¸êÀÚ CBaseSocket::~CBaseSocket() { // ¼Ò¸êÀÚ Socket ¸í½ÃÀû Close ó¸®. Close(); } /// @brief ¼ÒÄÏÀÇ Close 󸮸¦ ¼öÇàÇÔ. void CBaseSocket::Close() { m_bConnected = false; if( m_sock != SOCKET_NOT_VALID ) { close( m_sock ); m_sock = SOCKET_NOT_VALID; } } /// @brief Àü´Þ¹ÞÀº Target À¸·Î Socket Á¢¼ÓÀ» ¼öÇà /// @param szTarget [in] Á¢¼Ó ´ë»ó Host name ¶Ç´Â IP /// @param nPort [in] Á¢¼Ó Port /// @return Á¢¼Ó ¼º°ø½Ã true, ½ÇÆÐ½Ã false ¹Ýȯ. ( ¿À·ù ³»¿ªÀº ·Î±ë󸮵Ê.) bool CBaseSocket::Connect( const std::string& szTarget, int nPort ) { // ±âÁ¸ Á¢¼ÓÀ» Close ó¸® Close(); unsigned int nHost = ConversionAddr( szTarget.c_str() ); if( nHost == INADDR_NONE ) { LOG( LERR, "target host name resolve fail. [%s]->INADDR_NONE", szTarget.c_str()); return false; } struct sockaddr_in stTargetAddr; m_sock = socket(AF_INET, SOCK_STREAM, 0); if( m_sock == -1 ) { int errorNum = errno; LOG( LERR, "socket create failed.[%d][%s] Target:[%s]", errorNum, strerror(errorNum), szTarget.c_str() ); m_sock = SOCKET_NOT_VALID; return false; } stTargetAddr.sin_family = AF_INET; stTargetAddr.sin_addr.s_addr = nHost; stTargetAddr.sin_port = htons( nPort ); // NEW 2012-05-18 huibong // Connnection Á¾·á½Ã ¸¹Àº TIME_WAIT »óÅ ¹ß»ýÀ¸·Î ÀÎÇØ .. À̸¦ Á¦°ÅÇϱâ À§ÇØ SO_LINGER ¿É¼ÇÀ» ¼³Á¤Ã³¸®ÇÑ´Ù. struct linger opt_linger; opt_linger.l_onoff = 1; /* LINGER ON */ // CHG 2014-05-27 huibong // ¾Æ·¡¿Í °°ÀÌ No Wait ·Î ó¸®ÇÒ °æ¿ì... Server ¿ªÈ° ¼öÇà Áß Close ½ÅÈ£¸¦ ¹ÞÁö ¸øÇÏ´Â °æ¿ì°¡ ¹ß»ý °¡´ÉÇÔ. // µû¶ó¼­ 5 sec Á¤µµ ´ë±âÇϵµ·Ï ¼öÁ¤ ó¸®ÇÑ´Ù. //opt_linger.l_linger = 0; /* No Wait => 0 for abortive disconnect */ opt_linger.l_linger = 5; // 5 sec ´ë±â int result = setsockopt( m_sock, SOL_SOCKET, SO_LINGER, &opt_linger, sizeof(opt_linger)); if( result != 0 ) { int errorNum = errno; LOG( LERR, "setsockopt func SO_LINGER set fail.[%d][%s]", errorNum, strerror(errorNum)); } /* Send Timeout ¼³Á¤. */ struct timeval tv_timeo = { 5, 0 }; result = setsockopt( m_sock, SOL_SOCKET, SO_SNDTIMEO, &tv_timeo, sizeof(tv_timeo)); if( result != 0 ) { int errorNum = errno; LOG( LERR, "setsockopt func SO_SNDTIMEO set fail.[%d][%s]", errorNum, strerror(errorNum)); } if(connect(m_sock, (struct sockaddr *)&stTargetAddr, sizeof(stTargetAddr)) < 0 ) { int errorNum = errno; LOG( LERR, "connect fail.[%s][%d] [%d][%s]", szTarget.c_str(), nPort, errorNum, strerror( errorNum ) ); Close(); return false; } else { m_bConnected = true; return true; } } /// @brief ¸â¹ö º¯¼öÀÎ m_sock ÀÌ À¯È¿ÇÏ°í ¿¬°áµÈ »óÅÂÀÎ °æ¿ì true ¹Ýȯ. /// @return socketÀÌ À¯È¿ÇÏÁö ¾Ê°Å³ª ¿¬°áÀÌ ²÷¾îÁö °æ¿ì false ¹Ýȯ, ±×¿Ü¿¡´Â true ¹Ýȯ. bool CBaseSocket::IsValidSocket() { if( m_sock != SOCKET_NOT_VALID && m_bConnected == true ) return true; else return false; } /// @brief m_sock À¸·ÎºÎÅÍ ÁöÁ¤µÈ size ¸¸Å­ µ¥ÀÌÅÍ read ¸¦ ½Ãµµ ( read ÇÔ¼ö¿Í µ¿ÀÏ ) /// @param vptr [out] Read µÈ µ¥ÀÌÅ͸¦ ÀúÀåÇϱâ À§ÇÑ º¯¼ö¿¡ ´ëÇÑ Æ÷ÀÎÅÍ. /// @param size [in] read ÇϰíÀÚ ÇÏ´Â µ¥ÀÌÅÍÀÇ Å©±â. /// @return read µÈ µ¥ÀÌÅÍÀÇ Å©±â. 0: fd closed, -1: ¿À·ù ssize_t CBaseSocket::Read( void * vptr, size_t size ) { if( IsValidSocket() == false ) return 0; ssize_t nRead = 0; while( (nRead = read( m_sock, vptr, size )) < 0 ) { int errorNum = errno; if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK ) { nRead = 0; continue; } else if( errorNum == ECONNRESET || errorNum == EPIPE ) { // 2010-07-23 BUG huibong À߸øµÈ ´ëÀÔ¿¬»êÀÚ¸¦ ºñ±³¿¬»êÀÚ·Î ¼öÁ¤. // 2015-08-28 CHG huibong EPIPE(32) ¿À·ù ¹ß»ý½Ã.. ¿¬°á Á¾·á·Î 󸮵ǵµ·Ï ¼öÁ¤ m_bConnected = false; return 0; } else { LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) ); return -1; } } return nRead; } /// @brief m_sock ·ÎºÎÅÍ ÁöÁ¤µÈ size ¸¸Å­ read ¸¦ ½Ãµµ, \n ///< ¸¸¾à ÁöÁ¤µÈ Å©±â¸¸Å­ µ¥ÀÌÅͰ¡ Á¸ÀçÇÏÁö ¾ÊÀ» °æ¿ì ÇØ´ç Å©±â¸¸Å­ µ¥ÀÌÅ͸¦ read ÇÒ¶§±îÁö ´ë±â. /// @param vptr [out] Read µÈ µ¥ÀÌÅ͸¦ ÀúÀåÇϱâ À§ÇÑ º¯¼ö¿¡ ´ëÇÑ Æ÷ÀÎÅÍ. /// @param size [in] read ÇϰíÀÚ ÇÏ´Â µ¥ÀÌÅÍÀÇ Å©±â. /// @return read µÈ µ¥ÀÌÅÍÀÇ Å©±â. 0: fd closed, -1: ¿À·ù ssize_t CBaseSocket::ReadN( void * vptr, size_t size ) { if( IsValidSocket() == false ) return 0; ssize_t nRead = 0; size_t nLeft = size; char * ptr = (char *)vptr; while( nLeft > 0 ) { if( (nRead = read( m_sock, ptr, nLeft )) < 0 ) { int errorNum = errno; if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK ) { nRead = 0; continue; } else if( errorNum == ECONNRESET || errorNum == EPIPE ) { // 2015-08-28 CHG huibong EPIPE(32) ¿À·ù ¹ß»ý½Ã.. ¿¬°á Á¾·á·Î 󸮵ǵµ·Ï ¼öÁ¤ m_bConnected = false; return 0; } else { LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) ); return -1; } } else if( nRead == 0 ) { m_bConnected = false; return 0; } nLeft -= nRead; ptr += nRead; } return size; } /// @brief m_sock ·ÎºÎÅÍ ÁöÁ¤µÈ size ¸¸Å­ read ¸¦ ½Ãµµ, \n ///< ¸¸¾à ÁöÁ¤µÈ Å©±â¸¸Å­ µ¥ÀÌÅͰ¡ Á¸ÀçÇÏÁö ¾ÊÀ» °æ¿ì ÇØ´ç Å©±â¸¸Å­ µ¥ÀÌÅ͸¦ read ÇÒ¶§±îÁö ´ë±â. \n ///< ¶Ç´Â ÁöÁ¤µÈ Timeout °ª µ¿¾È read ¸¦ ÇÏÁö ¸øÇÏ´Â °æ¿ì ¿À·ù ó¸®. /// @param vptr [out] Read µÈ µ¥ÀÌÅ͸¦ ÀúÀåÇϱâ À§ÇÑ º¯¼ö¿¡ ´ëÇÑ Æ÷ÀÎÅÍ. /// @param size [in] read ÇϰíÀÚ ÇÏ´Â µ¥ÀÌÅÍÀÇ Å©±â. /// @param timeout [in] Timeout value (sec) /// @return read µÈ µ¥ÀÌÅÍÀÇ Å©±â. 0: fd closed, -1: ¿À·ù , -2: Timeout ssize_t CBaseSocket::ReadNTimeout( void * vptr, size_t size, int timeout ) { if( IsValidSocket() == false ) return 0; ssize_t nRead = 0; size_t nLeft = size; char * ptr = (char *)vptr; struct timeval timeOver; int result; fd_set selectFds; FD_ZERO( &selectFds ); while( nLeft > 0 ) { timeOver.tv_sec = timeout; timeOver.tv_usec = 0; FD_SET( m_sock, &selectFds ); result = select( m_sock+1, &selectFds, (fd_set *)NULL, (fd_set *)NULL, &timeOver ); if( result > 0 ) { if( FD_ISSET( m_sock, &selectFds )) { if( (nRead = read( m_sock, ptr, nLeft) ) < 0 ) { int errorNum = errno; if(errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK) { nRead = 0; continue; } else if( errorNum == ECONNRESET || errorNum == EPIPE ) { // 2015-08-28 CHG huibong EPIPE(32) ¿À·ù ¹ß»ý½Ã.. ¿¬°á Á¾·á·Î 󸮵ǵµ·Ï ¼öÁ¤ m_bConnected = false; return 0; } else { LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) ); return -1; } } else if( nRead == 0 ) { m_bConnected = false; return 0; } nLeft -= nRead; ptr += nRead; } } else if( result == 0 ) // Timeout { //LOG( LDBG, "read timeout"); return -2; } else { int errorNum = errno; LOG( LERR, "select func error.[%d][%s]", errorNum, strerror(errorNum) ); return -1; } } return size; } /// @brief m_sock ·ÎºÎÅÍ ÁöÁ¤µÈ size ¸¸Å­ read ¸¦ ½Ãµµ, \n ///< ¸¸¾à ÁöÁ¤µÈ Å©±â¸¸Å­ µ¥ÀÌÅͰ¡ Á¸ÀçÇÏÁö ¾ÊÀ» °æ¿ì ÇØ´ç Å©±â¸¸Å­ µ¥ÀÌÅ͸¦ read ÇÒ¶§±îÁö ´ë±â. \n ///< ¶Ç´Â ÁöÁ¤µÈ Timeout °ª µ¿¾È read ¸¦ ÇÏÁö ¸øÇÏ´Â °æ¿ì ¿À·ù ó¸®. ///< º» ÇÔ¼ö´Â Socket »ó¿¡ ÀÌ¹Ì Data °¡ Á¸ÀçÇÏ´Â °æ¿ì¿¡¸¸ »ç¿ë. /// @param vptr [out] Read µÈ µ¥ÀÌÅ͸¦ ÀúÀåÇϱâ À§ÇÑ º¯¼ö¿¡ ´ëÇÑ Æ÷ÀÎÅÍ. /// @param size [in] read ÇϰíÀÚ ÇÏ´Â µ¥ÀÌÅÍÀÇ Å©±â. /// @param timeout [in] Timeout value (sec) /// @return read µÈ µ¥ÀÌÅÍÀÇ Å©±â. 0: fd closed, -1: ¿À·ù, -2: Timeout ssize_t CBaseSocket::ReadNTimeout2( void * vptr, size_t size, int timeout ) { if( IsValidSocket() == false ) return 0; ssize_t nRead = 0; size_t nLeft = size; char * ptr = (char *)vptr; int errorNum; // ¿ì¼±Àº Àб⠽õµ. if( (nRead = read( m_sock, ptr, nLeft) ) < 0 ) { errorNum = errno; if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK ) { nRead = 0; } else if( errorNum == ECONNRESET || errorNum == EPIPE ) { // 2015-08-28 CHG huibong EPIPE(32) ¿À·ù ¹ß»ý½Ã.. ¿¬°á Á¾·á·Î 󸮵ǵµ·Ï ¼öÁ¤ m_bConnected = false; return 0; } else { LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) ); return -1; } } else if( nRead == 0 ) { m_bConnected = false; return 0; } nLeft -= nRead; ptr += nRead; // ¸¸¾à ´õ ¹ÞÀ» µ¥ÀÌÅͰ¡ Á¸ÀçÇÑ´Ù¸é. if( nLeft > 0 ) { struct timeval timeOver; int result; fd_set selectFds; FD_ZERO( &selectFds ); while( nLeft > 0 ) { timeOver.tv_sec = timeout; timeOver.tv_usec = 0; FD_SET( m_sock, &selectFds ); result = select( m_sock+1, &selectFds, (fd_set *)NULL, (fd_set *)NULL, &timeOver ); if( result > 0 ) { if( FD_ISSET( m_sock, &selectFds )) { if( (nRead = read( m_sock, ptr, nLeft) ) < 0 ) { errorNum = errno; if(errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK) { nRead = 0; continue; } else if( errorNum == ECONNRESET || errorNum == EPIPE ) { // 2015-08-28 CHG huibong EPIPE(32) ¿À·ù ¹ß»ý½Ã.. ¿¬°á Á¾·á·Î 󸮵ǵµ·Ï ¼öÁ¤ m_bConnected = false; return 0; } else { LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) ); return -1; } } else if( nRead == 0 ) { m_bConnected = false; return 0; } nLeft -= nRead; ptr += nRead; } } else if( result == 0 ) // Timeout { //LOG( LDBG, "read timeout"); return -2; } else { errorNum = errno; LOG( LERR, "select func error.[%d][%s]", errorNum, strerror(errorNum) ); return -1; } } } return size; } /// @brief m_sock À¸·Î ÁöÁ¤µÈ Å©±â¸¸Å­ vptr ÀÇ µ¥ÀÌÅ͸¦ Àü¼Û ½Ãµµ.\n ///< Send Timeout ¿É¼Ç ¼³Á¤À¸·Î Write Timeout ¼³Á¤ °¡´É. \n ///< ÁöÁ¤µÈ Ƚ¼ö¸¸Å­ ÀçÀü¼Û ½ÇÆÐ½Ã ¿À·ù·Î ó¸®ÇÔ. /// @param vptr [in] Àü´ÞÇÒ µ¥ÀÌÅ͸¦ ÀúÀåÇÑ º¯¼ö¿¡ ´ëÇÑ Æ÷ÀÎÅÍ. /// @param size [in] write ÇϰíÀÚ ÇÏ´Â µ¥ÀÌÅÍÀÇ Å©±â. /// @return write µÈ µ¥ÀÌÅÍÀÇ Å©±â. 0: fd closed, -1: ¿À·ù ssize_t CBaseSocket::WriteN( const void * vptr, size_t size ) { if( IsValidSocket() == false ) return 0; size_t nLeft; ssize_t nWrite; const char * ptr = (const char *)vptr; nLeft = size; int nTryCount = 0; while( nLeft > 0 ) { if( (nWrite = send( m_sock, ptr, nLeft, 0 )) < 0 ) { int errorNum = errno; if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK ) { nWrite = 0; ++nTryCount; } else if( errorNum == ECONNRESET || errorNum == EPIPE ) { // 2015-08-28 CHG huibong EPIPE(32) ¿À·ù ¹ß»ý½Ã.. ¿¬°á Á¾·á·Î 󸮵ǵµ·Ï ¼öÁ¤ m_bConnected = false; return 0; } else { LOG( LERR, "send func fail.[%d][%s]", errorNum, strerror(errorNum)); return -1; } } else if( nWrite == 0 ) { m_bConnected = false; return 0; } if( nTryCount > MAX_SEND_RETRY_COUNT ) { LOG( LERR, "send func failure due to exceeding count of retry[%d/%d]", nTryCount, MAX_SEND_RETRY_COUNT ); return 0 ; } nLeft -= nWrite; ptr += nWrite; } return size; } // CHG 2011-05-03 huibong // gethostbyname() ÇÔ¼ö°¡ Thread Safe ÇÏÁö ¾Ê±â ¶§¹®¿¡ // DNS resolve 󸮽à À߸øµÈ Á¤º¸¸¦ ¹ÝȯÇÒ °¡´É¼ºÀÌ Á¸Àç // ÀÌ¿¡ µû¶ó º» ÇÔ¼ö¸¦ ¼öÁ¤Ã³¸®ÇÔ. /* unsigned int CBaseSocket::ConversionAddr( const char * name ) { struct hostent *he; int max; unsigned int retval; if ((retval = inet_addr(name)) != INADDR_NONE) return retval; he = gethostbyname(name); if (he == NULL) return INADDR_NONE; for (max = 0; he->h_addr_list[max]; max++) ; if (max == 1) return *((unsigned int *)(he->h_addr_list[0])); else return *((unsigned int *)(he->h_addr_list[random() % max])); } */ unsigned int CBaseSocket::ConversionAddr( const char * name ) { unsigned int retval; struct addrinfo hints; struct addrinfo *result = NULL; int error; // Àü´Þ¹ÞÀº Á¤º¸°¡ IP ÁÖ¼ÒÀÎ °æ¿ì... // - DNS resolve ÇÒ ÇÊ¿ä ¾øÀÌ º¯È¯½ÃŲ °ªÀ» ±×´ë·Î »ç¿ëÇÑ´Ù. if( (retval = inet_addr(name)) != INADDR_NONE ) return retval; // getaddrinfo() ÇÔ¼ö È£ÃâÀ» À§ÇÑ Hint ¼³Á¤ memset( &hints, 0x00, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; // Thread Safe ÇÑ DNS Resolve ó¸®ÇÔ¼ö È£Ãâ // int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res); // CHG 2014-11-14 huibong // getaddrinfo È£Ãâ½Ã Á¾Á¾ EAI_NONAME ¿À·ù°¡ ¹ÝȯµÊ. (#20783) // - µû¶ó¼­ ¿À·ù ¹Ýȯ½Ã Àç½ÃµµÇϵµ·Ï ±â´É Ãß°¡ // CHG 2015-07-15 huibong // usleep ÀÇ multi thread »ó¿¡¼­ block ¹ß»ý °¡´É // - nanosleep À» »ç¿ëÅä·Ï º¯°æ ó¸® for( int count = 0 ; count < 3; count++ ) { error = getaddrinfo( name, NULL, &hints, &result ); // ¿À·ù ¹ß»ý½Ã if( error != 0 ) { if( result != NULL) { freeaddrinfo(result); result = NULL; } // Àá½Ã ´ë±â ÈÄ Àç½Ãµµ ó¸® struct timespec sleep; sleep.tv_sec = 0; sleep.tv_nsec = 500000000; // 0.5 sec nanosleep( &sleep, NULL ); } else { // Á¤»ó ó¸®µÈ °æ¿ì... // - loop Å»Ãâ break; } } // ÃÖÁ¾ ¿À·ù ¹ß»ý½Ã if( error != 0 ) { LOG( LERR, "name[%s] dns resolve fail. getaddrinfo return error [%d][%s]", name, error, gai_strerror(error) ); return INADDR_NONE; } struct sockaddr_in * addr = (struct sockaddr_in *)result->ai_addr; retval = (unsigned int)(addr->sin_addr.s_addr); // DNS Resovle °á°ú È®Àοë ÄÚµå /* struct addrinfo *temp; for( temp = result; temp; temp = temp->ai_next ) { addr = (struct sockaddr_in *)temp->ai_addr; printf ("getaddrinfo result = %s\n",inet_ntoa( addr->sin_addr)); } */ // getaddrinfo() ÇÔ¼ö¿¡¼­ »ý¼ºÇÑ ¸Þ¸ð¸® ¿µ¿ª ÇØÁ¦ ó¸®. freeaddrinfo(result); return retval; }