#include "FtsdSocketControl.h" #include #include #include #include #include #include #include // NEW 2016-05-12 huibong data 수신 실패시 timeout 처리 관련 기능 추가. #define FTSD_DATA_RECV_TIMEOUT 2 //sec ( 3 sec 이상일 경우.. Alive check 와 충돌로 timeout 처리가 안 될 수 있음.) /// @brief 생성자. CFtsdSocketControl::CFtsdSocketControl( ) : CBaseSocket( SOCKET_NOT_VALID ) , m_nPacketHeaderLen ( sizeof(m_packetHeader)) , m_nPacketDataLen( 0 ) { } /// @brief 소멸자 CFtsdSocketControl::~CFtsdSocketControl() { // 소멸자 Socket 명시적 Close 처리. Close(); } /// @brief 전달받은 Target 으로 Socket 접속을 수행 /// @param szTarget [in] 접속 대상 Host name 또는 IP /// @param nPort [in] 접속 Port /// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.) bool CFtsdSocketControl::ConnectTarget( const std::string& szTarget, int nPort ) { return Connect( szTarget, nPort ); } /// @brief Packet Header 정보를 Log 파일에 Logging 처리 void CFtsdSocketControl::PrintHeaderToLog() { _LOG( LINF, "------------------------------------" ); _LOG( LINF, "stx [%02x]", m_packetHeader.stx ); _LOG( LINF, "type [%02x]", m_packetHeader.type ); _LOG( LINF, "command [%02x][%02x][%02x][%02x]" , m_packetHeader.command[0], m_packetHeader.command[1] , m_packetHeader.command[2], m_packetHeader.command[3] ); _LOG( LINF, "result [%02x][%02x][%02x][%02x]" , m_packetHeader.result[0], m_packetHeader.result[1] , m_packetHeader.result[2], m_packetHeader.result[3] ); _LOG( LINF, "data_length [%u]", m_nPacketDataLen ); _LOG( LINF, "extend_code [%02x][%02x]", m_packetHeader.extend_code[0], m_packetHeader.extend_code[1] ); _LOG( LINF, "------------------------------------" ); } /// @brief Alive Check 를 위한 패킷 전송 => 해당 패킷의 응답은 필요없으므로 수신시 무시할 것. /// @return 전송 성공시 true, 실패시 false 반환. bool CFtsdSocketControl::SendAliveCheck() { if( IsValidSocket() == false ) return false; // 정보 요청을 위한 Packet Header 생성. struct FileTransferPacketHeader stHeader; memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader)); stHeader.stx = HEADER_STX_CODE; stHeader.type = HEADER_TYPE_REQUEST; stHeader.command[0] = COMMON_ALIVE_CHECK; // Packet Header 정보 전송 if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen ) return false; return true; } /// @brief 연결된 Socket 을 통해 Transfer Daemon 으로 File Replication 명령 전송. /// @param szFileName [in] 복제할 원본 Source 파일명 /// @param nFileSize [in] 복제할 원본 Source 파일의 크기. /// @param szTargetTranId [in[ RC 간 복제 처리시 변경할 대상 Tran ID 정보 값 /// @param nFileHashCheckLevel [in] File 에 대한 Hash Check Level 정보 /// @param bUseInternalIp [in] 내부망을 이용하여 파일 송수신을 수행할지 여부 /// @param vecTargetFhs [in] 복제할 Target FHS 정보 /// @return Replication 복제 요청 메시지 전송 성공시 true, 실패시 false 반환. bool CFtsdSocketControl::SendFileReplicationRequest(const std::string& szFileName, unsigned long long nFileSize , int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs) { std::string szTargetTranId; szTargetTranId.clear(); return SendFileReplicationRequest( szFileName, nFileSize, szTargetTranId, nFileHashCheckLevel, bUseInternalIp, vecTargetFhs); } bool CFtsdSocketControl::SendFileReplicationRequest(const std::string& szFileName, unsigned long long nFileSize , const std::string& szTargetTranId, int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs) { if( IsValidSocket() == false ) return false; // 정보 요청을 위한 Packet Header 생성. struct FileTransferPacketHeader stHeader; memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader)); stHeader.stx = HEADER_STX_CODE; stHeader.type = HEADER_TYPE_REQUEST; stHeader.command[0] = CONTROL_FILE_REPLICATION; // Data 부분의 길이를 계산한다. unsigned int nTemp = 0; nTemp = 4 + szFileName.size(); // File Name ( 4 + 가변 ) nTemp += 8; // File Size ( 8 Byte ) nTemp += ( 4 + szTargetTranId.size()); // Target Tran ID ( 4 + 가변 ) nTemp += 4; // File Hash check Level ( 4 Byte ) nTemp += 1; // 내부망 IP 사용 여부 ( 1 Byte ) nTemp += 4; // FHS Count ( 4 Byte ) std::vector< std::string >::const_iterator it; for( it = vecTargetFhs.begin(); it != vecTargetFhs.end() ; it++ ) { nTemp = nTemp + 4 + it->size() ; // Target FHS 정보 ( 4 + 가변 ) } stHeader.data_length = htonl( nTemp ); // Packet Header 정보 전송 if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen ) { LOG( LERR, "File Replication request send failed."); return false; } // Packet Data 부분 전송 // File Name 부분 nTemp = htonl( szFileName.size() ); WriteN( &nTemp, 4); WriteN( szFileName.c_str(), szFileName.size() ); // File Size 전송 WriteN( &nFileSize, 8 ); // Target Tran ID 부분 nTemp = htonl( szTargetTranId.size() ); WriteN( &nTemp, 4); if( szTargetTranId.size() > 0 ) WriteN( szTargetTranId.c_str(), szTargetTranId.size() ); // File Hash Check Level 전송 nTemp = htonl( nFileHashCheckLevel ); WriteN( &nTemp, 4); // 내부망 사용 여부 전달 if( bUseInternalIp == true ) m_tempBuffer[0] = 0x01; else m_tempBuffer[0] = 0x00; WriteN( m_tempBuffer, 1); // Target FHS Count 정보 전달. nTemp = htonl( vecTargetFhs.size() ); WriteN( &nTemp, 4); // Loop 를 돌면서 각 FHS 정보를 전달한다. for( it = vecTargetFhs.begin(); it != vecTargetFhs.end() ; it++ ) { nTemp = htonl( it->size() ); WriteN( &nTemp, 4); WriteN( it->c_str(), it->size()); } return true; } /// @brief File Replication 요청에 대한 처리 결과를 수신한다. /// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. /// @param bSuccess [out] File Replication 이 정상적으로 수행되었는지 여부 /// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보. /// @param mapSuccessFhs [out] Replication 에 대한 처리 성공시 성공한 FHS 및 File 정보를 저장 /// @param mapFailFhs [out] Replication 에 대한 처리 성공시 실패한 FHS 및 오류메시지 정보를 저장. /// @return -1 : Read 수신시 오류 발생시 => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n ///< 0 : nTimeout 에 지정된 시간안에 완료 응답을 받지 못한 경우. \n ///< 이 기능은 호출 함수의 Block 을 해제하기 위한 기능으로 \n ///< 이 값이 반환된 경우 다른 작업을 한 후 다시 호출하도록 한다. \n ///< 1 : 파일 복제 처리에 대한 응답을 수신한 경우, Output 인자를 확인할 것. \n ///< 2 : Transfer Daemon 으로 부터 Packet 을 수신하였으나 File Replication 요청에 대한 응답이 아닌 경우 \n ///< 해당 정보는 멤버변수 상에 저장된다. ( 단 Alive Packet 은 아님) int CFtsdSocketControl::GetFileReplicationResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage , std::map< std::string, std::string>& mapSuccessFhs , std::map< std::string, std::string>& mapFailFhs ) { // 복제 요청에 대한 응답 패킷 대기 // 수신 대기 Timeout 은 전달받은 값으로 설정. if( IsValidSocket() == false ) return -1; int nRead; bool isAlivePacket; do { isAlivePacket = false; // Socket 으로 부터 Packet Header 부분 수신. // 수신된 정보는 멤버변수에 저장처리. nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout ); // nRead 0: Socket Closed, -1: error , -2: Timeout 이므로 if( nRead == -2 ) return 0; // Timeout 반환. else if( nRead == 0 || nRead == -1 ) return -1; // Socket Close 또는 오류 발생시 // STX code 검사. if( m_packetHeader.stx != HEADER_STX_CODE ) { LOG( LERR, "Not valid stx code."); return -1; } // Data Length 부분 값을 멤버 변수에 저장처리. m_nPacketDataLen = ntohl( m_packetHeader.data_length ); // Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다. if( m_packetHeader.type == HEADER_TYPE_REQUEST && m_packetHeader.command[0] == COMMON_ALIVE_CHECK && m_nPacketDataLen == 0 ) { isAlivePacket = true; } } while( isAlivePacket == true ); // Alive Packet 이 아닌 경우 해당 Packet 에 대한 복제에 대한 응답패킷인지 검사. if( m_packetHeader.type != HEADER_TYPE_RESPONSE || m_packetHeader.command[0] != CONTROL_FILE_REPLICATION ) { // 다른 패킷이 들어온 경우 // ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만 // 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로 // 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다. GetPacketData( m_nPacketDataLen ); return 2; } // Replication 에 대한 응답 패킷인 경우 // 1. 성공, 실패 여부 판단. if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS ) { // 오류가 발생한 경우임 bSuccess = false; // Packet Data 상에서 오류 메시지를 수신받아 반환 처리. if( GetPacketData( m_nPacketDataLen, szErrorMessage ) == false ) { LOG( LERR, "Error msg receive failed."); return -1; } else return 1; } // 2. 성공인 경우 bSuccess = true; unsigned int nCount; unsigned int nLength; std::string szKey; std::string szValue; // 2.1 성공 목록의 Count 정보 수신 if( GetPacketData( 4 ) == false ) { LOG( LERR, "Success count receive failed."); return -1; } nCount = GetDataToUInt( m_tempBuffer, true); for( unsigned int i = 0; i < nCount; i++) { szKey.clear(); szValue.clear(); GetPacketData( 4 ); nLength = GetDataToUInt( m_tempBuffer, true); GetPacketData( nLength, szKey ); GetPacketData( 4 ); nLength = GetDataToUInt( m_tempBuffer, true); GetPacketData( nLength, szValue ); mapSuccessFhs[ szKey ] = szValue; } // 2.2 실패 목록의 Count 정보 수신 if( GetPacketData( 4 ) == false ) { LOG( LERR, "Failed count receive failed."); return -1; } nCount = GetDataToUInt( m_tempBuffer, true); for( unsigned int i = 0; i < nCount; i++) { szKey.clear(); szValue.clear(); GetPacketData( 4 ); nLength = GetDataToUInt( m_tempBuffer, true); GetPacketData( nLength, szKey ); GetPacketData( 4 ); nLength = GetDataToUInt( m_tempBuffer, true); GetPacketData( nLength, szValue ); mapFailFhs[ szKey ] = szValue; } return 1; } /// @brief 대상과 연결된 Socket 을 통해 Transfer Daemon 으로 File Cache 명령 전송. /// @param szFileName [in] Cache 처리할 원본 Source 파일명 (/stg/node0/186/abcde..) /// @param nFileSize [in] Cache 처리할 원본 Source 파일의 크기. /// @param nFileHashCheckLevel [in] File 에 대한 Hash Check Level 정보 ( conf 파일에 지정됨) /// @param bUseInternalIp [in] 내부망을 이용하여 파일 송수신을 수행할지 여부 \n ///< 해당값을 true 로 지정시 ftsd 상에서 내부망을 우선 사용하여 파일 복제 시도 ///< 만약 내부망 사용 불가시 자동으로 외부망 사용. /// @param vecTargetFhs [in] Cache 대상 Target FHS Host Name 정보 /// @return Cache 요청 메시지 전송 성공시 true, 실패시 false 반환. bool CFtsdSocketControl::SendFileCacheRequest(const std::string& szFileName, unsigned long long nFileSize , int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs) { if( IsValidSocket() == false ) return false; // 정보 요청을 위한 Packet Header 생성. struct FileTransferPacketHeader stHeader; memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader)); stHeader.stx = HEADER_STX_CODE; stHeader.type = HEADER_TYPE_REQUEST; stHeader.command[0] = CONTROL_FILE_CACHE; // Data 부분의 길이를 계산한다. unsigned int nTemp = 0; nTemp = 4 + szFileName.size(); // File Name ( 4 + 가변 ) nTemp += 8; // File Size ( 8 Byte ) nTemp += 4; // Target Tran ID ( 4 + 실제값은 사용 안하므로 0 ) nTemp += 4; // File Hash check Level ( 4 Byte ) nTemp += 1; // 내부망 IP 사용 여부 ( 1 Byte ) nTemp += 4; // FHS Count ( 4 Byte ) std::vector< std::string >::const_iterator it; for( it = vecTargetFhs.begin(); it != vecTargetFhs.end() ; it++ ) { nTemp = nTemp + 4 + it->size() ; // Target FHS 정보 ( 4 + 가변 ) } stHeader.data_length = htonl( nTemp ); // Packet Header 정보 전송 if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen ) { LOG( LERR, "File Cache request send failed."); return false; } // Packet Data 부분 전송 // File Name 부분 nTemp = htonl( szFileName.size() ); WriteN( &nTemp, 4); WriteN( szFileName.c_str(), szFileName.size() ); // File Size 전송 WriteN( &nFileSize, 8 ); // Target Tran ID 부분 nTemp = htonl( 0 ); WriteN( &nTemp, 4); // File Hash Check Level 전송 nTemp = htonl( nFileHashCheckLevel ); WriteN( &nTemp, 4); // 내부망 사용 여부 전달 if( bUseInternalIp == true ) m_tempBuffer[0] = 0x01; else m_tempBuffer[0] = 0x00; WriteN( m_tempBuffer, 1); // Target FHS Count 정보 전달. nTemp = htonl( vecTargetFhs.size() ); WriteN( &nTemp, 4); // Loop 를 돌면서 각 FHS 정보를 전달한다. for( it = vecTargetFhs.begin(); it != vecTargetFhs.end() ; it++ ) { nTemp = htonl( it->size() ); WriteN( &nTemp, 4); WriteN( it->c_str(), it->size()); } return true; } /// @brief File Cache 요청에 대한 처리 결과를 transfer daemon 으로 부터 수신한다. /// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n ///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환. /// @param bSuccess [out] File Cache 처리가 정상적으로 수행되었는지 여부 \n ///< trnsfer 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n ///< szErrorMessage 변수상에 오류내용이 저장됨. \n ///< 정상적으로 처리된 경우 true 가 저장되며 \n ///< mapSuccessFhs, mapFailFhs 상에 관련 정보가 저장됨. /// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장 /// @param mapSuccessFhs [out] bSuccess == true 인 경우 \n ///< Cache 처리에 성공한 FHS 및 저장된 File 이름 정보를 저장 /// @param mapFailFhs [out] bSuccess == true 인 경우 \n ///< Cache 처리에 실패한 FHS Host Name 및 발생된 오류메시지 정보를 저장. /// @return -1 : Socket 통신 관련 오류 발생하여 Transfer Daemon 과 연결이 끊어진 경우 \n ///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n ///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 Transfer Daemon으로부터 ///< Cache 에 대한 처리 결과 정보를 수신하지 못한 경우. \n ///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n ///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n ///< 응답을 대기할 수 있다. \n ///< 1 : 파일 Cache 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인 ///< 2 : Transfer Daemon 으로 부터 Data 을 수신하였으나 File Cache 요청에 대한 응답이 아닌 경우 \n ///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n ///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다. int CFtsdSocketControl::GetFileCacheResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage , std::map< std::string, std::string>& mapSuccessFhs , std::map< std::string, std::string>& mapFailFhs ) { // Cache 처리 요청에 대한 응답 패킷 대기 // 수신 대기 Timeout 은 전달받은 값으로 설정. if( IsValidSocket() == false ) return -1; int nRead; bool isAlivePacket; do { isAlivePacket = false; // Socket 으로 부터 Packet Header 부분 수신. // 수신된 정보는 멤버변수에 저장처리. nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout ); // nRead 0: Socket Closed, -1: error , -2: Timeout 이므로 if( nRead == -2 ) return 0; // Timeout 반환. else if( nRead == 0 || nRead == -1 ) return -1; // Socket Close 또는 오류 발생시 // STX code 검사. if( m_packetHeader.stx != HEADER_STX_CODE ) { LOG( LERR, "Not valid stx code."); return -1; } // Data Length 부분 값을 멤버 변수에 저장처리. m_nPacketDataLen = ntohl( m_packetHeader.data_length ); // Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다. if( m_packetHeader.type == HEADER_TYPE_REQUEST && m_packetHeader.command[0] == COMMON_ALIVE_CHECK && m_nPacketDataLen == 0 ) { isAlivePacket = true; } } while( isAlivePacket == true ); // Alive Packet 이 아닌 경우 해당 Packet 에 대한 Cache 처리 요청에 대한 응답패킷인지 검사. if( m_packetHeader.type != HEADER_TYPE_RESPONSE || m_packetHeader.command[0] != CONTROL_FILE_CACHE ) { // 다른 패킷이 들어온 경우 // ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만 // 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로 // 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다. GetPacketData( m_nPacketDataLen ); return 2; } // Cache 요청에 대한 응답 패킷인 경우 // 1. 성공, 실패 여부 판단. if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS ) { // 오류가 발생한 경우임 bSuccess = false; // Packet Data 상에서 오류 메시지를 수신받아 반환 처리. if( GetPacketData( m_nPacketDataLen, szErrorMessage ) == false ) { LOG( LERR, "Error msg receive failed."); return -1; } else return 1; } // 2. 성공인 경우 bSuccess = true; unsigned int nCount; unsigned int nLength; std::string szKey; std::string szValue; // 2.1 성공 목록의 Count 정보 수신 if( GetPacketData( 4 ) == false ) { LOG( LERR, "Success count receive failed."); return -1; } nCount = GetDataToUInt( m_tempBuffer, true); for( unsigned int i = 0; i < nCount; i++) { szKey.clear(); szValue.clear(); GetPacketData( 4 ); nLength = GetDataToUInt( m_tempBuffer, true); GetPacketData( nLength, szKey ); GetPacketData( 4 ); nLength = GetDataToUInt( m_tempBuffer, true); GetPacketData( nLength, szValue ); mapSuccessFhs[ szKey ] = szValue; } // 2.2 실패 목록의 Count 정보 수신 if( GetPacketData( 4 ) == false ) { LOG( LERR, "Failed count receive failed."); return -1; } nCount = GetDataToUInt( m_tempBuffer, true); for( unsigned int i = 0; i < nCount; i++) { szKey.clear(); szValue.clear(); GetPacketData( 4 ); nLength = GetDataToUInt( m_tempBuffer, true); GetPacketData( nLength, szKey ); GetPacketData( 4 ); nLength = GetDataToUInt( m_tempBuffer, true); GetPacketData( nLength, szValue ); mapFailFhs[ szKey ] = szValue; } return 1; } /// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Content Check 명령 전송. bool CFtsdSocketControl::SendFileCheckRequest(const std::string& szFileName, bool bHashCheck, unsigned long long nHashCheckSize ) { if( IsValidSocket() == false ) return false; // 정보 요청을 위한 Packet Header 생성. struct FileTransferPacketHeader stHeader; memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader)); stHeader.stx = HEADER_STX_CODE; stHeader.type = HEADER_TYPE_REQUEST; stHeader.command[0] = CONTROL_FILE_CHECK; // Data 부분의 길이를 계산한다. unsigned int nTemp = 0; nTemp = 4 + szFileName.size(); // File Name ( 4 + 가변 ) nTemp += 4; // File Hash 추출 여부 ( 4 Byte ) if( bHashCheck == true ) { nTemp += 8; // File Hash 추출 Size 정보 ( 8 Byte ) } stHeader.data_length = htonl( nTemp ); // Packet Header 정보 전송 if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen ) { LOG( LERR, "File[%s] Check request send failed.", szFileName.c_str() ); return false; } // Packet Data 부분 전송 // 1. File Name 부분 nTemp = htonl( szFileName.size() ); WriteN( &nTemp, 4); WriteN( szFileName.c_str(), szFileName.size() ); // 2. Hash 추출 여부 if( bHashCheck == true ) { nTemp = htonl( 1 ); WriteN( &nTemp, 4); // 3. Hash 추출 size (MByte 단위 ) 값 전송. WriteN( &nHashCheckSize, 8 ); } else { nTemp = htonl( 0 ); WriteN( &nTemp, 4); } return true; } /// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Unlink 명령 전송. bool CFtsdSocketControl::SendFileUnlinkRequest(const std::string& szFileName, unsigned long long nFileSize, bool bForceUnlink ) { if( IsValidSocket() == false ) return false; // 정보 요청을 위한 Packet Header 생성. struct FileTransferPacketHeader stHeader; memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader)); stHeader.stx = HEADER_STX_CODE; stHeader.type = HEADER_TYPE_REQUEST; stHeader.command[0] = CONTROL_FILE_UNLINK; // Data 부분의 길이를 계산한다. unsigned int nTemp = 0; nTemp = 4 + szFileName.size(); // File Name ( 4 + 가변 ) nTemp += 8; // File Size ( 8 Byte ) nTemp += 4; // Unlink Mode ( 4 Byte ) stHeader.data_length = htonl( nTemp ); // Packet Header 정보 전송 if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen ) { LOG( LERR, "File[%s] Unlink request send failed.", szFileName.c_str() ); return false; } // Packet Data 부분 전송 // 1. File Name 부분 nTemp = htonl( szFileName.size() ); WriteN( &nTemp, 4); WriteN( szFileName.c_str(), szFileName.size() ); // 2. File Size 부분 WriteN( &nFileSize, 8 ); // 3. Unlink Mode 정보 if( bForceUnlink == true ) { nTemp = htonl( UNLINK_MODE_FORCE ); } else { nTemp = htonl( UNLINK_MODE_NORMAL ); } WriteN( &nTemp, 4); return true; } /// @brief File Check 요청에 대한 처리 결과를 ftsd 로 부터 수신한다. int CFtsdSocketControl::GetFileCheckResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage, unsigned long long& nFileSize, std::string& szHashValue ) { if( IsValidSocket() == false ) return -1; int nRead; bool isAlivePacket; do { isAlivePacket = false; // Socket 으로 부터 Packet Header 부분 수신. // 수신된 정보는 멤버변수에 저장처리. nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout ); // nRead 0: Socket Closed, -1: error , -2: Timeout 이므로 if( nRead == -2 ) return 0; // Timeout 반환. else if( nRead == 0 || nRead == -1 ) return -1; // Socket Close 또는 오류 발생시 // STX code 검사. if( m_packetHeader.stx != HEADER_STX_CODE ) { LOG( LERR, "Not valid stx code."); return -1; } // Data Length 부분 값을 멤버 변수에 저장처리. m_nPacketDataLen = ntohl( m_packetHeader.data_length ); // Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다. if( m_packetHeader.type == HEADER_TYPE_REQUEST && m_packetHeader.command[0] == COMMON_ALIVE_CHECK && m_nPacketDataLen == 0 ) { isAlivePacket = true; } } while( isAlivePacket == true ); // Alive Packet 이 아닌 경우 해당 Packet 에 대한 Check에 대한 응답 패킷인지 검사. if( m_packetHeader.type != HEADER_TYPE_RESPONSE || m_packetHeader.command[0] != CONTROL_FILE_CHECK ) { // 다른 패킷이 들어온 경우 // ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만 // 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로 // 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다. GetPacketData( m_nPacketDataLen ); return 2; } // Check 에 대한 응답 패킷이 경우. // 1. 성공, 실패 여부 판단. if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS ) { // 오류가 발생한 경우임 bSuccess = false; // Packet Data 상에서 오류 메시지를 수신받아 반환 처리. if( GetPacketData( m_nPacketDataLen, szErrorMessage ) == false ) { LOG( LERR, "Error msg receive failed."); return -1; } else return 1; } // 2. 성공인 경우 bSuccess = true; // 2.1 File Size 정보 추출.. if( GetPacketData( 8 ) == false ) { LOG( LERR, "File size receive fail."); return -1; } nFileSize = GetDataToUInt64( m_tempBuffer ); // 2.2 File Hash 필드의 Length 정보 추출. if( GetPacketData( 4 ) == false ) { LOG( LERR, "Hash length receive fail."); return -1; } unsigned int nLength = GetDataToUInt( m_tempBuffer, true); if( nLength > 0 ) { // 추출된 Hash 정보 저장. GetPacketData( nLength, szHashValue ); } return 1; } /// @brief File Unlink 요청에 대한 처리 결과를 ftsd 로 부터 수신한다. int CFtsdSocketControl::GetFileUnlinkResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage ) { if( IsValidSocket() == false ) return -1; int nRead; bool isAlivePacket; do { isAlivePacket = false; // Socket 으로 부터 Packet Header 부분 수신. // 수신된 정보는 멤버변수에 저장처리. nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout ); // nRead 0: Socket Closed, -1: error , -2: Timeout 이므로 if( nRead == -2 ) return 0; // Timeout 반환. else if( nRead == 0 || nRead == -1 ) return -1; // Socket Close 또는 오류 발생시 // STX code 검사. if( m_packetHeader.stx != HEADER_STX_CODE ) { LOG( LERR, "Not valid stx code."); return -1; } // Data Length 부분 값을 멤버 변수에 저장처리. m_nPacketDataLen = ntohl( m_packetHeader.data_length ); // Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다. if( m_packetHeader.type == HEADER_TYPE_REQUEST && m_packetHeader.command[0] == COMMON_ALIVE_CHECK && m_nPacketDataLen == 0 ) { isAlivePacket = true; } } while( isAlivePacket == true ); // Alive Packet 이 아닌 경우 해당 Packet 에 대한 Unlink 에 대한 응답 패킷인지 검사. if( m_packetHeader.type != HEADER_TYPE_RESPONSE || m_packetHeader.command[0] != CONTROL_FILE_UNLINK ) { // 다른 패킷이 들어온 경우 // ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만 // 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로 // 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다. GetPacketData( m_nPacketDataLen ); return 2; } // Unlink 에 대한 응답 패킷이 경우. // 1. 성공, 실패 여부 판단. if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS ) { // 오류가 발생한 경우임 bSuccess = false; // Packet Data 상에서 오류 메시지를 수신받아 반환 처리. if( GetPacketData( m_nPacketDataLen, szErrorMessage ) == false ) { LOG( LERR, "Error msg receive failed."); return -1; } else return 1; } // 2. 성공인 경우 bSuccess = true; // 2.1 성공인 경우 Data 부분이 존재하지 않으므로.. Pass.. return 1; } // NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460) /// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Content Check 명령 전송. (토토디스크 지원용) /// @param szFileName [in] 원본 Source 파일명 (/stg/node0/186/abcde..) /// @return Check 요청 메시지 전송 성공시 true, 실패시 false 반환. bool CFtsdSocketControl::SendFileCheckTOTORequest( const std::string& szFileName ) { if( IsValidSocket() == false ) return false; // 정보 요청을 위한 Packet Header 생성. struct FileTransferPacketHeader stHeader; memset( &stHeader, 0x00, sizeof( struct FileTransferPacketHeader ) ); stHeader.stx = HEADER_STX_CODE; stHeader.type = HEADER_TYPE_REQUEST; stHeader.command[0] = CONTROL_FILE_CHECK_TOTO; // Data 부분의 길이를 계산한다. unsigned int nTemp = 0; nTemp = 4 + szFileName.size(); // File Name ( 4 + 가변 ) stHeader.data_length = htonl( nTemp ); // Packet Header 정보 전송 if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen ) { LOG( LERR, "TOTO File[%s] Check request send failed.", szFileName.c_str() ); return false; } // Packet Data 부분 전송 // 1. File Name 부분 nTemp = htonl( szFileName.size() ); WriteN( &nTemp, 4 ); WriteN( szFileName.c_str(), szFileName.size() ); return true; } // NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460, #27533) /// @brief File Check 요청에 대한 처리 결과를 ftsd 로 부터 수신한다. int CFtsdSocketControl::GetFileCheckTOTOResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage , unsigned long long& nFileSize, std::string& szTotoMd5, std::string& szChecksum, std::string& szDnaCheckKey ) { if( IsValidSocket() == false ) return -1; int nRead; bool isAlivePacket; do { isAlivePacket = false; // Socket 으로 부터 Packet Header 부분 수신. // 수신된 정보는 멤버변수에 저장처리. nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout ); // nRead 0: Socket Closed, -1: error , -2: Timeout 이므로 if( nRead == -2 ) return 0; // Timeout 반환. else if( nRead == 0 || nRead == -1 ) return -1; // Socket Close 또는 오류 발생시 // STX code 검사. if( m_packetHeader.stx != HEADER_STX_CODE ) { LOG( LERR, "Not valid stx code." ); return -1; } // Data Length 부분 값을 멤버 변수에 저장처리. m_nPacketDataLen = ntohl( m_packetHeader.data_length ); // Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다. if( m_packetHeader.type == HEADER_TYPE_REQUEST && m_packetHeader.command[0] == COMMON_ALIVE_CHECK && m_nPacketDataLen == 0 ) { isAlivePacket = true; } } while( isAlivePacket == true ); // Alive Packet 이 아닌 경우 해당 Packet 에 대한 Check에 대한 응답 패킷인지 검사. if( m_packetHeader.type != HEADER_TYPE_RESPONSE || m_packetHeader.command[0] != CONTROL_FILE_CHECK_TOTO ) { // 다른 패킷이 들어온 경우 // ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만 // 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로 // 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다. GetPacketData( m_nPacketDataLen ); return 2; } // Check 에 대한 응답 패킷이 경우. // 1. 성공, 실패 여부 판단. if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS ) { // 오류가 발생한 경우임 bSuccess = false; // Packet Data 상에서 오류 메시지를 수신받아 반환 처리. if( GetPacketData( m_nPacketDataLen, szErrorMessage ) == false ) { LOG( LERR, "Error msg receive failed." ); return -1; } else return 1; } // 2. 성공인 경우 bSuccess = true; // 2.1 File Size 정보 추출.. if( GetPacketData( 8 ) == false ) { LOG( LERR, "File size receive fail." ); return -1; } nFileSize = GetDataToUInt64( m_tempBuffer ); unsigned int nLength = 0; // 2.2 totorasa 용 MD5 Hash 값 정보 수신 및 저장 if( GetPacketData( 4 ) == false ) { LOG( LERR, "szTotoMd5 length receive fail." ); return -1; } nLength = GetDataToUInt( m_tempBuffer, true ); if( nLength > 0 ) { // 추출된 정보 저장. GetPacketData( nLength, szTotoMd5 ); } // 2.3 totorasa 용 Checksum 값 정보 수신 및 저장 if( GetPacketData( 4 ) == false ) { LOG( LERR, "szChecksum length receive fail." ); return -1; } nLength = GetDataToUInt( m_tempBuffer, true ); if( nLength > 0 ) { // 추출된 정보 저장. GetPacketData( nLength, szChecksum ); } // 2.4 totorasa 용 DNA CHECK Key 값 정보 수신 및 저장 if( GetPacketData( 4 ) == false ) { LOG( LERR, "szDnaCheckKey length receive fail." ); return -1; } nLength = GetDataToUInt( m_tempBuffer, true ); if( nLength > 0 ) { // 추출된 정보 저장. GetPacketData( nLength, szDnaCheckKey ); } return 1; } // NEW 2021-04-01 huibong content stat(size, atime, mtime, ctime) 정보 추출 기능 추가 (#33516) /// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Content stat 명령 전송. /// @param szFileName [in] 원본 Source 파일명 (/stg/node0/186/abcde..) /// @return stat 요청 메시지 전송 성공시 true, 실패시 false 반환. bool CFtsdSocketControl::SendFileStatRequest( const std::string & szFileName ) { if( IsValidSocket() == false ) return false; // 정보 요청을 위한 Packet Header 생성. struct FileTransferPacketHeader stHeader; memset( &stHeader, 0x00, sizeof( struct FileTransferPacketHeader ) ); stHeader.stx = HEADER_STX_CODE; stHeader.type = HEADER_TYPE_REQUEST; stHeader.command[0] = CONTROL_FILE_STAT; // Data 부분의 길이를 계산한다. unsigned int nTemp = 0; nTemp = 4 + szFileName.size(); // File Name ( 4 + 가변 ) stHeader.data_length = htonl( nTemp ); // Packet Header 정보 전송 if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen ) { LOG( LERR, "File[%s] Stat request send failed.", szFileName.c_str() ); return false; } // Packet Data 부분 전송 // 1. File Name 부분 nTemp = htonl( szFileName.size() ); WriteN( &nTemp, 4 ); WriteN( szFileName.c_str(), szFileName.size() ); return true; } // NEW 2021-04-01 huibong content stat(size, atime, mtime, ctime) 정보 추출 기능 추가 (#33516) int CFtsdSocketControl::GetFileStatResult( int nTimeout, bool & bSuccess, std::string & szErrorMessage, long & nFileSize, long & nAccessTime, long & nModifyTime, long & nChangeTime ) { if( IsValidSocket() == false ) return -1; int nRead; bool isAlivePacket; do { isAlivePacket = false; // Socket 으로 부터 Packet Header 부분 수신. // 수신된 정보는 멤버변수에 저장처리. nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout ); // nRead 0: Socket Closed, -1: error , -2: Timeout 이므로 if( nRead == -2 ) return 0; // Timeout 반환. else if( nRead == 0 || nRead == -1 ) return -1; // Socket Close 또는 오류 발생시 // STX code 검사. if( m_packetHeader.stx != HEADER_STX_CODE ) { LOG( LERR, "Not valid stx code." ); return -1; } // Data Length 부분 값을 멤버 변수에 저장처리. m_nPacketDataLen = ntohl( m_packetHeader.data_length ); // Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다. if( m_packetHeader.type == HEADER_TYPE_REQUEST && m_packetHeader.command[0] == COMMON_ALIVE_CHECK && m_nPacketDataLen == 0 ) { isAlivePacket = true; } } while( isAlivePacket == true ); // Alive Packet 이 아닌 경우 해당 Packet 에 대한 Check에 대한 응답 패킷인지 검사. if( m_packetHeader.type != HEADER_TYPE_RESPONSE || m_packetHeader.command[0] != CONTROL_FILE_STAT ) { // 다른 패킷이 들어온 경우 // ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만 // 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로 // 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다. GetPacketData( m_nPacketDataLen ); return 2; } // STAT 에 대한 응답 패킷이 경우. // 1. 성공, 실패 여부 판단. if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS ) { // 오류가 발생한 경우임 bSuccess = false; // Packet Data 상에서 오류 메시지를 수신받아 반환 처리. if( GetPacketData( m_nPacketDataLen, szErrorMessage ) == false ) { LOG( LERR, "Error msg receive failed." ); return -1; } else return 1; } // 2. 성공인 경우 bSuccess = true; // 2.1 File Size 정보 추출.. if( GetPacketData( 8 ) == false ) { LOG( LERR, "File size receive fail." ); return -1; } nFileSize = GetDataToLong( m_tempBuffer ); // 2.2 File access time 정보 추출.. if( GetPacketData( 8 ) == false ) { LOG( LERR, "File access time receive fail." ); return -1; } nAccessTime = GetDataToLong( m_tempBuffer ); // 2.3 File modify time 정보 추출.. if( GetPacketData( 8 ) == false ) { LOG( LERR, "File modify time receive fail." ); return -1; } nModifyTime = GetDataToLong( m_tempBuffer ); // 2.4 File change time 정보 추출.. if( GetPacketData( 8 ) == false ) { LOG( LERR, "File change time receive fail." ); return -1; } nChangeTime = GetDataToLong( m_tempBuffer ); return 1; } /////////////////////////////////////////////////////////////////////////////////////// /// @brief Packet Header 부분의 수신 처리를 위한 함수. /// @param timeout [in] 대기시간. /// @retrun 성공시 true, 오류 발생및 실패시 fasle 반환. bool CFtsdSocketControl::GetPacketHeader( int timeout ) { if( IsValidSocket() == false ) return false; int nRead; bool isAlivePacket; do { isAlivePacket = false; // Socket 으로 부터 Packet Header 부분 수신. // 수신된 정보는 멤버변수에 저장처리. nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, timeout ); // 오류 발생시 if( nRead <= 0 ) return false; // STX code 검사. if( m_packetHeader.stx != HEADER_STX_CODE ) { LOG( LERR, "Not valid stx code."); return false; } // Data Length 부분 값을 멤버 변수에 저장처리. m_nPacketDataLen = ntohl( m_packetHeader.data_length ); // Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다. if( m_packetHeader.type == HEADER_TYPE_REQUEST && m_packetHeader.command[0] == COMMON_ALIVE_CHECK && m_nPacketDataLen == 0 ) { isAlivePacket = true; } } while( isAlivePacket == true ); return true; } /// @brief socket 에서 지정된 크기만큼의 데이터를 읽어 출력변수에 저장처리. /// @param size [in] read 할 데이터 크기 /// @param value [out] 읽은 데이터를 저장할 string 변수 /// @return On success return true, otherwise return false. bool CFtsdSocketControl::GetPacketData( unsigned int& size, std::string& value ) { // 1. 수신할 크기가 임시버퍼보다 큰 경우 새로운 버퍼를 생성한다. BYTE * pBuffer = NULL; bool bNewBufferCreated = false; if( size > DEFAULT_SOCKET_TEMP_BUFFER_SIZE ) { pBuffer = new BYTE[size]; // 신규 메모리 할당. bNewBufferCreated = true; } else pBuffer = m_tempBuffer; // 2. 데이터 수신 처리. int nRead = ReadNTimeout( pBuffer, size, FTSD_DATA_RECV_TIMEOUT ); // 오류 발생시 if( nRead <= 0 ) { // 신규로 생성된 버퍼인 경우 메모리 해제 처리. if( bNewBufferCreated == true ) delete [] pBuffer; return false; } // 3. 수신 데이터 저장 처리 - 기존 데이터 뒤에 붙여 준다=> 이게 사용하기 편함. value.append( (char *)pBuffer, size ); // 신규로 생성된 버퍼인 경우 메모리 해제 처리. if( bNewBufferCreated == true ) delete [] pBuffer; return true; } /// @brief socket 에서 지정된 크기만큼의 데이터를 읽어 내부 임시버퍼인 m_tempBuffer 에 저장처리. /// @param size [in] read 할 데이터 크기 /// @return On success return true, otherwise return false. bool CFtsdSocketControl::GetPacketData( unsigned int size ) { // 1. 수신할 크기가 임시버퍼보다 큰 경우 오류 반환. if( size > DEFAULT_SOCKET_TEMP_BUFFER_SIZE ) return false; // 2. 데이터 수신 처리. int nRead = ReadNTimeout( m_tempBuffer, size, FTSD_DATA_RECV_TIMEOUT ); // 오류 발생시 if( nRead <= 0 ) return false; // 3. 정상인 경우는 ReadNTimeout 함수가 무조건 요청 크기만큼 read 처리하므로 정상수신임. return true; } /// @brief 전달받은 데이터를 unsigned int 형으로 변환처리 및 Endian 변환 unsigned int CFtsdSocketControl::GetDataToUInt( BYTE * pValue, bool bConvertEndian ) { BYTE tempBuffer[4]; unsigned int result; memcpy(tempBuffer, pValue, 4); unsigned int * pInt = (unsigned int *)tempBuffer; // Endian 변환처리. if( bConvertEndian == true ) { result = ntohl( *pInt ); } else result = *pInt; return result; } unsigned long long CFtsdSocketControl::GetDataToUInt64( BYTE * pValue ) { unsigned long long * pResult; BYTE tempBuffer[8]; memcpy(tempBuffer, pValue, 8); pResult = (unsigned long long *)tempBuffer; return *pResult; } long CFtsdSocketControl::GetDataToLong( BYTE * pValue ) { long * pResult; BYTE tempBuffer[8]; memcpy( tempBuffer, pValue, 8 ); pResult = ( long * )tempBuffer; return *pResult; }