#include "SocketControl.h"
#include "FileHash.h"		// File Hash 검사를 위한 Class include

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

// 본 Class 에서 내부적으로 사용할 Packet 수신 결과 매크로 정의
// 본 정의값은 GetPacketHeader() 함수의 결과값임.
#define PACKET_HEADER_RECEIVE_SUCCESS			 1	// Packet Header 정보 수신 정상 처리
#define PACKET_HEADER_RECEIVE_TIMEOUT			 0	// Packet Header 정보 수신시 Timeout 발생한 경우
#define PACKET_HEADER_RECEIVE_SOCK_CLOSED		-1	// Packet Header 정보 수신시 socket 이 Close 된 경우
#define PACKET_HEADER_RECEIVE_INVALID_PROTOCOL	-2	// Packet Header 정보 수신시 잘못된 Packet 형식인 경우
#define PACKET_HEADER_RECEIVE_READ_ERROR		-3	// Packet Header 정보 수신시 시스템 함수 오류 발생한 경우


/// @brief 생성자.
CSocketControl::CSocketControl()
	: CBaseSocket( SOCKET_NOT_VALID )
	  , m_nPacketHeaderLen ( sizeof(m_packetHeader))
	  , m_nPacketDataLen( 0 )
{
}

/// @brief 생성자.
/// @param socket [in] 처리할 socket descriptor
CSocketControl::CSocketControl( const int& socket )
	: CBaseSocket( socket )
	  , m_nPacketHeaderLen ( sizeof(m_packetHeader))
	  , m_nPacketDataLen( 0 )
{
}


/// @brief 소멸자
CSocketControl::~CSocketControl()
{
	// 소멸자 Socket 명시적 Close 처리.
	Close();
}

/// @brief 전달받은 Target 으로 Socket 접속을 수행
/// @param szTarget [in] 접속 대상 Host name 또는 IP
/// @param nPort	[in] 접속 Port
/// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
bool CSocketControl::ConnectTarget( const std::string& szTarget, int nPort )
{
	return Connect( szTarget, nPort );
}

/// @brief Packet 의 Header 정보를 수신하여 각 정보를 전달받은 참조변수로 반환.
/// @param bRequest [out] Request 요청인지 여부
/// @param bFromControl [out] Control 로 부터 전달된 Command 인지 여부
/// @param command [out] Command 정보
/// @return 1 : Data 수신 성공시
///         0 : Timeout 발생시
///        -1 : 접속 종료시
int CSocketControl::GetHeaderInfo( bool& bRequest, bool& bFromControl, char& command )
{
	if( IsValidSocket() == false )
		return -1;

	// Packet Header 정보 수신처리.
	// Socket 응답에 대한 대기상태( select) 에서 응답이 들어온 경우 이를 바로 처리하기 위해
	// Timeout 시간을 Default 에서 3 sec 로 변경처리함.
	int nResult = GetPacketHeader(3);

	// Header Data 수신 실패시
	if( nResult != PACKET_HEADER_RECEIVE_SUCCESS )
	{
		if( nResult == PACKET_HEADER_RECEIVE_TIMEOUT )	// Timeout 발생시
		{
			if( IsValidSocket() == false )	// socket 종료 여부를 다시 한번 확인
				return -1;
			else
				return 0;
		}
		else
		{
			// 잘못된 프로토콜 형식인 경우
			if( nResult == PACKET_HEADER_RECEIVE_INVALID_PROTOCOL )
			{
				// Socket 강제 종료 처리. 
				LOG( LERR, "Not valid protocol header. Connection closed by forced.");
				Close();
			}

			return -1;
		}
	}

	// Data 수신 성공시
	// Type 값 확인
	if( m_packetHeader.type == HEADER_TYPE_REQUEST )
		bRequest = true;

	// Command - Control Daemon 으로 부터 전송된 패킷인 경우.	
	if( m_packetHeader.command[0] != NOT_CONTROL_COMMAND )
	{
		bFromControl = true;
		command = m_packetHeader.command[0];
	}
	else
	{
		// 내부 FTSD Daemon 간 통신인 경우.
		bFromControl = false;
		command = m_packetHeader.command[1];
	}

	return 1;
}

/// @brief Packet 의 Header 정보 중 현재 전달받은 Header 의 Result 값을 반환
/// @param result [out] result 4 Byte 부분의 값을 저장하기 위한 참조변수
/// @return result 4 Byte 부분 중 첫번째 Byte 값을 반환.
char CSocketControl::GetHeaderResult( )
{
	return m_packetHeader.result[0];
}

void CSocketControl::GetHeaderResult( std::vector<char>& result )
{
	result.clear();
	result.push_back( m_packetHeader.result[0] );
	result.push_back( m_packetHeader.result[1] );
	result.push_back( m_packetHeader.result[2] );
	result.push_back( m_packetHeader.result[3] );
}

/// @brief Packet Header 정보를 Log 파일에 Logging 처리
void CSocketControl::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 Transfer Daemon 으로 Local NIC IP 정보 전송
/// @param bResult [in] szValue 에 저장된 Local IP 정보의 유효여부
/// @param szValue [in] Local NIC IP 정보
/// @return 전송 성공시 true, 실패시 false 반환.
bool CSocketControl::SendLocalIpInfo( bool bResult, const std::string& szValue )
{
	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_RESPONSE;
	stHeader.command[0] = NOT_CONTROL_COMMAND;
	stHeader.command[1] = TRANSFER_LOCAL_IP;

	// szValue 에 저장된 정보가 유효하지 않은 경우.
	if( bResult == false )
	{
		stHeader.result[0] = HEADER_RESULT_ERROR;
		stHeader.data_length = htonl( 0 );
	}
	else
	{
		// 1. result 를 성공으로 설정
		// 2. data_length 부분을 전송할 문자열 크기로 지정
		stHeader.result[0] = HEADER_RESULT_SUCCESS;
		stHeader.data_length = htonl( szValue.size() );
	}

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
		return false;
	
	// Data 부분 존재시 Data 부분 전송처리
	if( bResult == true )
	{
		WriteN( szValue.c_str(), szValue.size());
	}

	return true;
}

/// @brief Socket 으로 연결된 대상의 Local NIC IP 정보를 요청한 후 해당 정보 수신.
bool CSocketControl::GetLocalIpInfo( std::string& szValue )
{
	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]		= NOT_CONTROL_COMMAND;
	stHeader.command[1]		= TRANSFER_LOCAL_IP;
	stHeader.data_length	= htonl( 0 );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
		return false;

	// Local IP 정보 결과 수신 대기 - 대기중 Alive Packet 은 무시 처리.
	// 수신 대기 Timeout 은 60 Sec 으로 설정.
	int nResult = GetPacketHeader(60);

	// Header Data 수신 실패시
	if( nResult != PACKET_HEADER_RECEIVE_SUCCESS )
	{
		// 잘못된 프로토콜 형식인 경우
		if( nResult == PACKET_HEADER_RECEIVE_INVALID_PROTOCOL )
		{
			// Socket 강제 종료 처리. 
			LOG( LERR, "Not valid protocol header. Connection closed by forced.");
			Close();
		}

		return false;
	}

	// 수신된 데이터 확인
	if( m_packetHeader.type == HEADER_TYPE_RESPONSE	
			&& m_packetHeader.command[0] == NOT_CONTROL_COMMAND 
			&& m_packetHeader.command[1] == TRANSFER_LOCAL_IP )
	{
		// Result 값 확인
		if( m_packetHeader.result[0] == HEADER_RESULT_SUCCESS )
		{
			if( GetPacketData( m_nPacketDataLen, szValue ) == true )
			{
				return true;
			}
		}
	}

	return false;	
}

/// @brief 파일의 수신 요청을 받은 경우 해당 업무를 수행하기 위한 함수
///< 본 함수는 파일의 수신, 수신 관련 통신 작업을 내부적으로 진행 처리함.
bool CSocketControl::ReceiveFile( const std::string& szStorageRoot, unsigned long long nMaxStorageAvailableSize, char commandCode )
{

	// 1. 파일 수신 요청에 대한 Packet Data 부분을 읽어 저장처리.
	// Header 정보는 이미 수신한 상태이므로
	if( m_nPacketDataLen <= 0 )
		return false;

	// 1.1 Data 부분의 크기만큼 Read 처리하여 임시버퍼에 저장.
	if( GetPacketData( m_nPacketDataLen ) == false )
		return false;

	BYTE * pPos = m_tempBuffer;
	unsigned int nLength;
	std::string szFileName;
	std::string szErrorMessage;

	// 1.2 File Name 파싱
	nLength = GetDataToUInt( pPos, true);
	pPos += 4;
	szFileName.append( (char *)pPos, nLength );
	pPos += nLength;

	// 1.3 File Size 파싱
	unsigned long long nFileSize = GetDataToUInt64( pPos );

	// 전달받은 인자 및 저장할 Mount Position 정보에 대한 유효성 검사.	

	if( szFileName.empty() == true )
	{	
		// 오류 메시지 전송.
		szErrorMessage.append( "File name is empty.");
		SendFileReceiveResponse( false, szErrorMessage, commandCode );

		LOG( LERR, "SocketControl::ReceiveFile: Receive filename is empty.");
		return false;
	}

	// file size  0 일수도 있고, 변수는 unsigned long long 이므로 검사하지 않는다.

	// Mount 저장 위치 검사.
	// 2014-03-04 CHG huibong
	// CProperStorage 객체 변경 및 본 함수의 인자 nMaxStorageAvailableSize 추가에 따른 검사 및 오류 처리 로직 추가
	if( szStorageRoot.empty() == true )
	{
		// 최적 Mount 경로 추출이 실패한 경우...
		szErrorMessage.append( "File save path not valid (Storage save path not found).");
		SendFileReceiveResponse( false, szErrorMessage, commandCode );

		LOG( LERR, "SocketControl::ReceiveFile: Proper mount info invalid. file[%s].", szFileName.c_str() );
		return false;


	}
	else
	{
		// 최적 Mount 경로 추출은 성공...
		// 해당 Mount 에 현재 파일 크기가 저장 가능한지 검사..
		if( nFileSize > nMaxStorageAvailableSize )
		{
			// 최적 Mount 경로의 용량 부족으로 인해 현재 파일을 저장할 수 없는 경우..
			szErrorMessage.append( "Not enough free space for save.");
			SendFileReceiveResponse( false, szErrorMessage, commandCode );

			LOG( LERR, "SocketControl::ReceiveFile: Not enough free space [%s][%llu]. file[%s][%llu]."
					, szStorageRoot.c_str(), nMaxStorageAvailableSize, szFileName.c_str(), nFileSize );

			return false;
		}
		else
		{
			// NEW 2014-11-03 huibong
			// - MakeDirectory 함수 수정으로 인해 mkdir -p 옵션 사용 불가
			// - 이로 인해 cache 인 경우.. /cache 폴더가 없는 경우.. 생성 불가 발생 
			// - 따라서 cache 폴더가 생성되도록 코드 추가 (기존 존재하는 경우에는 생성 안됨)
			MakeDirectory( szStorageRoot );
		}
	}


	// 2. 정상적인 경우 파일 fd 생성 및 Open 처리.
	// 파일 전체 경로 설정.
	std::string szFullFileName = szStorageRoot + "/" + szFileName ;

	// 파일의 저장 폴더 정보를 확인하여 없는 경우 생성해 버린다.
	string::size_type pos = szFullFileName.rfind( '/' );
	if( pos == string::npos )
	{
		// 파일 정보가 잘못된 경우.

		// CHG 2014-02-26 huibong
		// 다음과 같이 std::string apppend() 함수를 잘못 사용할 경우... 프로세스 죽거나.. 먹통 현상 발생..
		// szErrorMessage.append( "File full name not valid.[%s]", szFullFileName.c_str());
		// 이에 수정 처리함.
		char tempBuffer[1024];
		snprintf( tempBuffer, sizeof(tempBuffer) -1, "File full name not valid.[%s]", szFullFileName.c_str() );
		szErrorMessage.append( tempBuffer );
		
		// 오류 메시지 전송.
		SendFileReceiveResponse( false, szErrorMessage, commandCode );

		LOG( LERR, "SocketControl::ReceiveFile: %s", szErrorMessage.c_str() );
		return false;
	}
	else
	{
		std::string szDirectoryPath = szFullFileName.substr( 0, pos );
		MakeDirectory( szDirectoryPath );
	}

	_LOG( LDBG, "SocketControl::ReceiveFile: File receiver start [%s]", szFullFileName.c_str() );

	
	// Check the file existence.

	// CHG 2015-06-16 huibong
	// 물리 파일이 이미 존재하는 경우... overwrite 처리를 하지 않고.. 다음과 같이 처리한다.
	// 1. 물리 Size 가 다른 경우
	//    - 실패로 처리
	// 2. 물리 Size 가 동일한 경우.
	//    - 원본 content 수신 처리 생략
	//    - 이후 hash 비교, size 비교 등의 절차 수행되도록
	//    - 만약 위의 비교 절차 실패시 unlink 처리는 생략.
	
	bool bFileAlreadyExist = false;

	struct stat st;
	if( stat( szFullFileName.c_str(), &st ) == 0)
	{
		// 물리 파일이 이미 존재하는 경우.... Size 비교
		if( (unsigned long long)(st.st_size) == nFileSize )
		{
			// 물리 파일이 이미 존재하고.. 크기도 동일한 경우...
			bFileAlreadyExist = true;
			
			_LOG( LWAR, "SocketControl::ReceiveFile: File already exists. [%s] local[%jd] request[%llu].", szFullFileName.c_str(), st.st_size, nFileSize );
		}
		else
		{
			// 물리 파일이 이미 존재하지만.. 크기가 서로 다른 경우.
			// - 오류 메시지 전송 처리.
			char tempBuffer[1024];
			snprintf( tempBuffer, sizeof( tempBuffer ) - 1, "File already exist. but size not match. [%s] local[%jd] request[%llu]"
					            , szFullFileName.c_str(), st.st_size, nFileSize );

			szErrorMessage.append( tempBuffer );

			// 오류 메시지 전송.
			SendFileReceiveResponse( false, szErrorMessage, commandCode );

			LOG( LERR, "SocketControl::ReceiveFile: %s", szErrorMessage.c_str() );
			return false;
		}		
	}
	else
	{
		// stat fail
		if( errno != ENOENT )
		{
			LOG( LERR, "SocketControl::ReceiveFile: File stat check error[%d] [%s].", errno, szFullFileName.c_str() );
		}
	}
	

	// File Descriptor 생성.

	// CHG 2015-06-16 huibong
	// 기존 파일이 존재하는 경우... fopen, fclose 처리하면.. file data 가 날라 가므로.... fopen 처리 않도록한다.
	FILE * pFile = NULL;

	if( bFileAlreadyExist == false )
	{
		pFile = fopen( szFullFileName.c_str(), "w" );
		if( pFile == NULL )
		{
			// 파일 Open 실패시

			int errorNum = errno;

			// CHG 2014-02-26 huibong
			// 다음과 같이 std::string apppend() 함수를 잘못 사용할 경우... 프로세스 죽거나.. 먹통 현상 발생..
			// szErrorMessage.append( "File open failed to write.[%s]", szFullFileName.c_str());
			// 이에 수정 처리함.
			char tempBuffer[1024];
			snprintf( tempBuffer, sizeof( tempBuffer ) - 1, "File open failed to write.[%s] [%d][%s]", szFullFileName.c_str(), errorNum, strerror( errorNum ) );
			szErrorMessage.append( tempBuffer );

			// 오류 메시지 전송.
			SendFileReceiveResponse( false, szErrorMessage, commandCode );

			LOG( LERR, "SocketControl::ReceiveFile: %s", szErrorMessage.c_str() );
			return false;
		}
	}


	// 3. 파일 생성 및 정상 처리를 위한 Descriptor 생성 성공시 
	// 성공 메시지 전달. => 이러면 바로 다음에  TRANSFER_FILE_START Header 와 함께 파일이 날라온다.
	if( SendFileReceiveResponse( true, szErrorMessage, commandCode ) == false )
	{
		LOG( LERR, "SocketControl::ReceiveFile: File[%s] transfer start message send failed.", szFullFileName.c_str());

		if( pFile != NULL )
			fclose(pFile);

		return false;
	}

	// 4. 파일 전송 시작 Packet 대기
	// 수신 대기 Timeout 은 180 Sec 으로 설정.
	int nHeaderReceiveResult = GetPacketHeader(180);

	// Header Data 수신 실패시
	if( nHeaderReceiveResult != PACKET_HEADER_RECEIVE_SUCCESS )
	{
		LOG( LERR, "SocketControl::ReceiveFile: File[%s] transfer start packet wait timeout.", szFullFileName.c_str());
		
		if( pFile != NULL )
			fclose(pFile);

		// 잘못된 프로토콜 형식인 경우
		if( nHeaderReceiveResult == PACKET_HEADER_RECEIVE_INVALID_PROTOCOL )
		{
			// Socket 강제 종료 처리. 
			LOG( LERR, "SocketControl::ReceiveFile: Not valid protocol header. Connection closed by forced.");
			Close();
		}

		return false;
	}

	_LOG( LDBG, "SocketControl::ReceiveFile: Wait receive TRANSFER_FILE_START [%s]", szFullFileName.c_str() );
	
	// 5. 수신된 패킷이 File 전송 시작 패킷인지 검사한 후 
	if( m_packetHeader.type != HEADER_TYPE_REQUEST
			|| m_packetHeader.command[0] != NOT_CONTROL_COMMAND 
			|| m_packetHeader.command[1] != TRANSFER_FILE_START )
	{
		LOG( LERR, "SocketControl::ReceiveFile: File[%s] TRANSFER_FILE_START receive failed", szFullFileName.c_str());

		if( pFile != NULL )
			fclose(pFile);

		return false;
	}

	// 6. 파일 수신 및 저장소에 write 시작
	bool bFoundError = false;

	if( bFileAlreadyExist == false )
	{
		// 기존 물리 파일이 존재하지 않을 경우. 수신 및 file write 처리

		if( ReceiveToFile( pFile, nFileSize, szErrorMessage ) == false )
		{
			LOG( LERR, "RECEIVER:File[%s] receive fail.", szFullFileName.c_str() );

			if( pFile != NULL )
				fclose( pFile );

			bFoundError = true;

			// File 수신 실패시 해당 파일은 삭제처리한다.
			unlink( szFullFileName.c_str() );
		}
		else
		{
			// 정상적으로 데이터 수신 및 File write가 완료된 경우.
			if( pFile != NULL )
				fclose( pFile );

			bFoundError = false;
		}
	}
	else
	{
		// 기존 물리 파일이 존재하는 경우.. socket data 수신만 처리. file write 처리는 하지 않음
		if( ReceiveToDummy( nFileSize, szErrorMessage ) == false )
		{
			LOG( LERR, "RECEIVER:File[%s] receive fail.", szFullFileName.c_str() );

			bFoundError = true;
		}
		else
		{
			// 정상적으로 데이터 수신처리 된 경우.			
			bFoundError = false;
		}
	}

	// 수신 처리 완료 후.. 물리 파일 크기를 다시 체크..
	// - 동일 파일에 대한 write 로 발생 가능성으로 인해.. 크기가 변경될 가능성이 존재하므로.. 다시 한번 크기 체크.

	if( bFoundError == false )
	{
		if( stat( szFullFileName.c_str(), &st ) == 0 )
		{
			// 물리 파일 크기가 write 한 크기와 동일하지 않은 경우..
			// - write 오류 또는 다른 놈이 write 해서 깨진 경우임.
			if( ( unsigned long long )(st.st_size) != nFileSize )
			{
				char tempBuffer[1024];
				snprintf( tempBuffer, sizeof( tempBuffer ) - 1, "File write success. but size not match. [%s] local[%jd] request[%llu]"
						  , szFullFileName.c_str(), st.st_size, nFileSize );

				szErrorMessage.append( tempBuffer );
				
				LOG( LERR, "SocketControl::ReceiveFile: %s", szErrorMessage.c_str() );
				bFoundError = true;

				// unlink 하고 싶지만.. 다른 놈이 write 하고 있을 수 있으므로.. 
				// 아래 로직에서 socket 통신 오류인 경우에만 unlink 처리하도록 한다.

			}
		}
		else
		{
			// stat fail
			if( errno != ENOENT )
			{
				LOG( LERR, "SocketControl::ReceiveFile: File stat recheck error[%d] [%s].", errno, szFullFileName.c_str() );
			}
		}
	}
	
	

	_LOG( LDBG, "SocketControl::ReceiveFile: File receiver write success. [%s]", szFullFileName.c_str() );

	// 7. 다음 Packet Header 가 TRANSFER_FILE_END 인지 판단.
	// 만약 해당 패킷이 아니면 잘못된 file 이므로 수신된 파일 삭제처리한다.
	// 수신 대기 Timeout 은 180 Sec 으로 설정.	
	nHeaderReceiveResult = GetPacketHeader(180);

	// Header Data 수신 실패시
	if( nHeaderReceiveResult != PACKET_HEADER_RECEIVE_SUCCESS )
	{
		LOG( LERR, "SocketControl::ReceiveFile: File[%s] transfer file end packet wait timeout.", szFullFileName.c_str());

		// 정상적인 파일이 아니므로 삭제처리.
		// - 신규 생성된 파일인 경우에만 삭제 처리.
		if( bFileAlreadyExist == false )
			unlink( szFullFileName.c_str() );

		// 잘못된 프로토콜 형식인 경우
		if( nHeaderReceiveResult == PACKET_HEADER_RECEIVE_INVALID_PROTOCOL )
		{
			// Socket 강제 종료 처리. 
			LOG( LERR, "SocketControl::ReceiveFile: Not valid protocol header. Connection closed by forced.");
			Close();
		}

		return false;
	}

	// 8. 수신된 패킷이 File 전송 종료 패킷인지 검사한 후 
	if( m_packetHeader.type != HEADER_TYPE_REQUEST
			|| m_packetHeader.command[0] != NOT_CONTROL_COMMAND 
			|| m_packetHeader.command[1] != TRANSFER_FILE_END )
	{
		LOG( LERR, "SocketControl::ReceiveFile: File[%s] Not transfer file end packet for wait.", szFullFileName.c_str());

		// 정상적인 파일이 아니므로 삭제처리.
		// - 신규 생성된 파일인 경우에만 삭제 처리.
		if( bFileAlreadyExist == false )
			unlink( szFullFileName.c_str() );

		return false;
	}

	// 9. File 수신 완료(TRANSFER_FILE_END)에 대한 응답 패킷 전송
	if( SendFileEndResponse( !bFoundError, szFullFileName, nFileSize, szErrorMessage) == false )
	{
		LOG( LERR, "SocketControl::ReceiveFile: File[%s] Transfer file end packet send failed.", szFullFileName.c_str());

		// 정상적인 처리가 아니므로 삭제처리.
		// - 신규 생성된 파일인 경우에만 삭제 처리.
		if( bFileAlreadyExist == false )
			unlink( szFullFileName.c_str() );
		
		return false;
	}
	else
	{
		LOG( LDBG, "SocketControl::ReceiveFile: File Receive process COMPLETE [%s][%s][%s]"
				, szFullFileName.c_str(), (bFoundError == true ? "ERROR" : "SUCCESS")
				, szErrorMessage.c_str());

		// 자기가 생성한 파일에 오류가 발생한 경우에는 최종 unlink 처리한다.
		if( bFoundError == true && bFileAlreadyExist == false )
			unlink( szFullFileName.c_str() );
		
		return true;
	}	
}


/// @brief 연결된 socket 으로 파일 수신 요청 패킷을 전송한다.
bool CSocketControl::SendFileReceiveRequest( std::string& szFileName, unsigned long long nFileSize, char commandCode )
{
	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]		= NOT_CONTROL_COMMAND;
	stHeader.command[1]		= commandCode;

	stHeader.data_length	= htonl( 4 + szFileName.size() + 8 );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "File recevie request send failed [%s]", szFileName.c_str() );
		return false;
	}
	else
	{
		// Packet Data 부분 전송.
		// File Name Length 부분
		unsigned int nTemp = htonl( szFileName.size() );
		WriteN( &nTemp, 4);
		WriteN( szFileName.c_str(), szFileName.size() );
	
		// File Size 전송
		WriteN( &nFileSize, 8 );
	}
	
	return true;
}

/// @brief File 수신 요청에 대한 응답 패킷 전송( TRANSFER_FILE_REPLICATION, TRANSFER_FILE_CACHE 요청에 대한 response 패킷임.
/// 해당 패킷이 정상인 경우 이 패킷을 수신한 Daemon 은 파일 전송 시작 패킷 및 File의 전송을 바로 시작한다.
bool CSocketControl::SendFileReceiveResponse( bool bSuccess, std::string& szErrorString, char commandCode )
{
	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_RESPONSE;
	stHeader.command[0]		= NOT_CONTROL_COMMAND;
	stHeader.command[1]		= commandCode;

	// 검사 성공인 경우.
	if( bSuccess == true )
	{
		stHeader.result[0]   = HEADER_RESULT_SUCCESS; // 결과는 성공으로		
		stHeader.data_length = htonl( 0 );
	}
	else
	{
		// File 수신 요청 처리에 대한 오류 발생시
		stHeader.result[0]   = HEADER_RESULT_ERROR;
		stHeader.data_length = htonl( szErrorString.size() );
	}

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "Receive response packet send fail for file receive request.");
		return false;
	}

	// Packet Data 부분 전송
	if( bSuccess == false && szErrorString.size() > 0 )
	{
		WriteN( szErrorString.c_str(), szErrorString.size());
	}

	return true;
}


/// @brief File 수신 요청에 대한 응답 패킷을 수신처리하기 위한 함수( TRANSFER_FILE_REPLICATION, TRANSFER_FILE_CACHE 요청에 대한 response 처리).
/// 본 패킷이 정상인 경우 이 패킷을 수신한 Daemon 은 파일 전송 시작 패킷 및 File의 전송을 바로 시작한다.
bool CSocketControl::GetFileReceiveResponse( char commandCode, bool& bSuccess, std::string& szErrorString )
{
	if( IsValidSocket() == false )
		return false;

	// 2010-03-18 CHG huibong
	// 데이터가 몰리는 경우 파일 전송에 대한 요청에 대한 응답이 늦게 올 수가 있어
	// Timeout 값을 Default 에서 3분으로 변경함.
	int nResult = GetPacketHeader(180);

	// Header Data 수신 실패시
	if( nResult != PACKET_HEADER_RECEIVE_SUCCESS )
	{
		// 잘못된 프로토콜 형식인 경우
		if( nResult == PACKET_HEADER_RECEIVE_INVALID_PROTOCOL )
		{
			// Socket 강제 종료 처리. 
			LOG( LERR, "Not valid protocol header. Connection closed by forced.");
			Close();
		}

		return false;
	}

	// 수신된 데이터 확인
	if( m_packetHeader.type == HEADER_TYPE_RESPONSE	
			&& m_packetHeader.command[0] == NOT_CONTROL_COMMAND 
			&& m_packetHeader.command[1] == commandCode )
	{
		// Result 값 확인
		if( m_packetHeader.result[0] == HEADER_RESULT_SUCCESS )
		{
			// 성공인 경우.
			bSuccess = true;			
		}
		else
		{
			// 오류 정보가 존재하는 경우.
			bSuccess = false;
			GetPacketData( m_nPacketDataLen, szErrorString );
		}

		return true;
	}

	// 다른 응답패킷인 경우.

	return false;
}


/// @brief File 송신 시작(TRANSFER_FILE_START::REQUEST) 요청 패킷.
bool CSocketControl::SendFileStartRequest()
{
	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]		= NOT_CONTROL_COMMAND;
	stHeader.command[1]		= TRANSFER_FILE_START;

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "TRANSFER_FILE_START Request send fail.");

		return false;
	}

	return true;
}

/// @brief File 송신 완료후 해당 파일 정보 요청을 위한 (TRANSFER_FILE_END::REQUEST) 요청 패킷.
bool CSocketControl::SendFileEndRequest()
{
	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]		= NOT_CONTROL_COMMAND;
	stHeader.command[1]		= TRANSFER_FILE_END;

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "Tranaser file end request send fail." );

		return false;
	}

	return true;
}

/// @brief File 송신 완료(TRANSFER_FILE_END) 패킷에 대한 응답 패킷 전송
bool CSocketControl::SendFileEndResponse( bool bSuccess, std::string& szFileFullName
		, unsigned long long nFileSize, std::string& szErrorString )
{
	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_RESPONSE;
	stHeader.command[0]		= NOT_CONTROL_COMMAND;
	stHeader.command[1]		= TRANSFER_FILE_END;

	// 실패인 경우
	if( bSuccess == false )
	{
		stHeader.result[0] = HEADER_RESULT_ERROR;
		stHeader.data_length = htonl( szErrorString.size() );
	}
	else
	{
		// 성공인 경우 => 파일 전체명( mount 정보 포함), 파일 크기 정보를 전송.
		stHeader.result[0] = HEADER_RESULT_SUCCESS;
		stHeader.data_length = htonl( 4 + szFileFullName.size() + 8 );
	}
	
	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "Transfer end response send fail." );
		return false;
	}

	// Packet Data 부분 전송
	if( bSuccess == false )
	{
		if( szErrorString.size() > 0 )
			WriteN( szErrorString.c_str(), szErrorString.size());
	}
	else
	{
		// File Name Length 부분
		unsigned int nTemp = htonl( szFileFullName.size() );
		WriteN( &nTemp, 4);
		WriteN( szFileFullName.c_str(), szFileFullName.size() );
	
		// File Size 전송
		WriteN( &nFileSize, 8 );
	}

	return true;
}

/// @brief 파일 수신 완료에 따른 응답패킷을 파싱처리.
bool CSocketControl::GetFileEndResult( bool& bSuccess, std::string& szFileFullName
		, unsigned long long& nFileSize, std::string& szErrorString )
{
	if( IsValidSocket() == false )
		return false;

	// 수신 대기 Timeout 은 180 sec 값으로 설정.
	int nResult = GetPacketHeader(180);

	// Header Data 수신 실패시
	if( nResult != PACKET_HEADER_RECEIVE_SUCCESS )
	{
		// 잘못된 프로토콜 형식인 경우
		if( nResult == PACKET_HEADER_RECEIVE_INVALID_PROTOCOL )
		{
			// Socket 강제 종료 처리. 
			LOG( LERR, "Not valid protocol header. Connection closed by forced.");
			Close();
		}

		return false;
	}

	// 수신된 데이터 확인
	if( m_packetHeader.type == HEADER_TYPE_RESPONSE	
			&& m_packetHeader.command[0] == NOT_CONTROL_COMMAND 
			&& m_packetHeader.command[1] == TRANSFER_FILE_END )
	{
		// Result 값 확인
		if( m_packetHeader.result[0] == HEADER_RESULT_SUCCESS )
		{
			// 성공인 경우. - 파일명, 파일 크기 부분 추출.
			bSuccess = true;

			// 1.1 Data 부분의 크기만큼 Read 처리하여 임시버퍼에 저장.
			if( GetPacketData( m_nPacketDataLen ) == false )
				return false;

			BYTE * pPos = m_tempBuffer;
			unsigned int nLength;
			std::string szFileName;
			// 1.2 File Full Name 파싱
			nLength = GetDataToUInt( pPos, true);
			pPos += 4;
			szFileFullName.append( (char *)pPos, nLength );
			pPos += nLength;

			// 1.3 File Size 파싱
			nFileSize = GetDataToUInt64( pPos );
		}
		else
		{
			// 오류 정보가 존재하는 경우.
			bSuccess = false;
			GetPacketData( m_nPacketDataLen, szErrorString );
		}

		return true;
	}

	// 다른 응답패킷인 경우.

	return false;
}


/// @brief 특정 파일에 대한 Hash 검사 요청을 연결된 Socket 을 통해 전송한다.
bool CSocketControl::SendHashCheckRequest(const std::string& szFileName, unsigned long long nFileSize
		, unsigned int nHashLevel, const std::string& szSourceHash )
{
	if( m_nPacketDataLen <= 0 )
		return false;

	// 정보 전송을 위한 메시지 Header 생성
	struct FileTransferPacketHeader stHeader;
	memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader));

	stHeader.stx			= HEADER_STX_CODE;
	stHeader.type			= HEADER_TYPE_REQUEST;
	stHeader.command[0]		= NOT_CONTROL_COMMAND;
	stHeader.command[1]		= TRANSFER_FILE_HASH;

	unsigned int nTemp = 0;
	nTemp = 4 + szFileName.size() + 8 + 4 + 4 + szSourceHash.size() ;
	
	stHeader.data_length = htonl( nTemp );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "Hash check request send failed.");
		return false;
	}

	// 각 Data 부분 전송
	
	// File Name Length 부분
	nTemp = htonl( szFileName.size() );
	WriteN( &nTemp, 4);
	WriteN( szFileName.c_str(), szFileName.size() );
	
	// File Size 전송
	WriteN( &nFileSize, 8 );

	// Hash Level 전송
	nTemp = htonl( nHashLevel );
	WriteN( &nTemp, 4);

	// 원본 Hash 값 전송
	nTemp = htonl( szSourceHash.size() );
	WriteN( &nTemp, 4);
	WriteN( szSourceHash.c_str(), szSourceHash.size() );
	
	return true;
}

/// @brief File 에 대한 Hash 검사 요청을 받은 경우 이를 처리, 해당 결과를 전송하기 위한 함수.
bool CSocketControl::CheckFileHash()
{
	// 1. 파일 Hash 검사에 요청에 대한 처리를 수행하기 위해
	// Packet Data 을 수신하여 파일 전체경로, Hash Level, Hash 값 정보를 수신한다.
	// Header 정보는 이미 수신한 상태이므로
	if( m_nPacketDataLen <= 0 )
		return false;

	// 1.1 Data 부분의 크기만큼 Read 처리하여 임시버퍼에 저장.
	if( GetPacketData( m_nPacketDataLen ) == false )
		return false;

	BYTE * pPos = m_tempBuffer;
	unsigned int nLength;
	std::string szFileName;
	// 1.2 File Full Name 파싱
	nLength = GetDataToUInt( pPos, true);
	pPos += 4;
	szFileName.append( (char *)pPos, nLength );
	pPos += nLength;

	// 1.3 File Size 파싱
	//unsigned long long nFileSize = GetDataToUInt64( pPos );
	pPos += 8;
	
	// 1.4 Hash Level 파싱
	unsigned int nHashLevel = GetDataToUInt( pPos, true);
	pPos += 4;
	
	// 1.5 Source Hash Value 파싱 
	std::string szSourceHashValue;
	nLength = GetDataToUInt( pPos, true);
	pPos += 4;
	szSourceHashValue.append( (char *)pPos, nLength );

	// 1.6 전달받은 각 인자의 유효성 검사.
	bool bFoundError = false;
	std::string szErrorMessage;
	if( szFileName.size() <= 0 )
	{
		bFoundError = true;
		szErrorMessage.append( "Receiver hash check: File name not valid.\n");
	}
	// file size  0 일수도 있고, 변수는 unsigned long long 이므로 검사하지 않는다.
	if( nHashLevel <=0 || nHashLevel > 2)
	{
		bFoundError = true;
		szErrorMessage.append( "Receiver hash check: Hash level not valid.\n");
	}
	if( szSourceHashValue.size() <= 0 )
	{
		bFoundError = true;
		szErrorMessage.append( "Receiver hash check: Source hash value not valid.\n");
	}
	
	if( bFoundError == true )
	{
		// 3. Hash 검사 수행 결과를 요청 Socket 으로 전달한다.
		// 전달받은 값이 유효하지 않은 경우 오류 메시지 전송.
		SendHashCheckResponse( false, szErrorMessage );
		LOG( LERR, "%s", szErrorMessage.c_str() );
		
		return false;
	}

	LOG( LDBG, "Receiver Hash check start[%s][%d][%s]" , szFileName.c_str(), nHashLevel, szSourceHashValue.c_str() );


	// 2. Hash 처리를 위한 객체를 생성한 후 Hash 검사를 수행
	CFileHash fileHash;
	if( fileHash.CheckFileHash( szFileName, nHashLevel, szSourceHashValue) == false )
	{
		// 3. Hash 검사 수행 결과를 요청 Socket 으로 전달한다.
		// 전달받은 값이 유효하지 않은 경우 오류 메시지 전송.
		szErrorMessage = fileHash.GetLastErrorMessage();
		SendHashCheckResponse( false, szErrorMessage );

		LOG( LERR, "Hash compare failed[%s]", szErrorMessage.c_str() );

		return false;
	}
	else
	{
		LOG( LDBG, "Receiver Hash check success.[%s][%d][%s]", szFileName.c_str(), nHashLevel, szSourceHashValue.c_str() );
	}

	// 3. Hash 검사 성공시 해당 결과 전송.
	szErrorMessage.clear();
	if( SendHashCheckResponse( true, szErrorMessage ) == false )
		return false;
	else
		return true;
}

/// @brief Hash Check 검사 요청에 대한 결과값을 전송한다.
bool CSocketControl::SendHashCheckResponse( bool bSuccess, std::string& szErrorString )
{
	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_RESPONSE;
	stHeader.command[0]		= NOT_CONTROL_COMMAND;
	stHeader.command[1]		= TRANSFER_FILE_HASH;

	// Hash 검사 성공인 경우.
	if( bSuccess == true )
	{
		stHeader.result[0] = HEADER_RESULT_SUCCESS;
		stHeader.data_length = htonl( 0 );
	}
	else
	{
		// Hash 검사 오류 발생시
		stHeader.result[0] = HEADER_RESULT_ERROR;
		stHeader.data_length = htonl( szErrorString.size() );
	}

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "Hash compare result send failed.");
		return false;
	}

	// Packet Data 부분 전송
	if( bSuccess == false && szErrorString.size() > 0 )
	{
		WriteN( szErrorString.c_str(), szErrorString.size());
	}

	return true;
}

/// @brief Hash Check 검사 요청에 대한 결과값을 수신한다.
bool CSocketControl::GetHashCheckResult( bool& bSuccess, std::string& szErrorString )
{
	if( IsValidSocket() == false )
		return false;

	// 루프를 돌면서 응답 결과가 수신되기를 기다린다.
	// 만약 특정한 시간( 5분 * 36 = 180분 = 3 시간) 동안 응답이 없는 경우 에러로 처리한다.
	int nFailCount = 0;
	int nResult;
	while(1)
	{
		nResult = GetPacketHeader(300);

		if( nResult == PACKET_HEADER_RECEIVE_SUCCESS )		// 응답 패킷을 수신한 경우.
		{
			break;
		}
		else if( nResult == PACKET_HEADER_RECEIVE_TIMEOUT )	// Timeout 발생시
		{
			nFailCount++;
			SendAliveCheck();	// 우선 Alive 한번 쏴 보고.

			// Socket 연결의 유효성 검사.
			if( IsValidSocket() == false )
			{
				// Socket 이 유효하지 않은 경우 오류 처리.
				LOG( LERR, "Hash check result get fail by socket error");
				return false;
			}

			// 아직 수신 대기 해야 하는 경우.=> 지정된 시간 기다렸는지 확인
			if( nFailCount >= 36 )
			{
				LOG( LERR, "Hash check result get fail by wait timeout [3 hour]");
				return false;
			}
		}
		else
		{
			// Socket 종료 또는 잘못된 protocol 형식인 경우
			if( nResult == PACKET_HEADER_RECEIVE_INVALID_PROTOCOL )
			{
				// Socket 강제 종료 처리. 
				LOG( LERR, "Not valid protocol header. Connection closed by forced.");
				Close();
			}

			LOG( LERR, "Hash check result get fail by socket closed");
			return false;
		}
	}
	
	// 수신된 데이터 확인
	if( m_packetHeader.type == HEADER_TYPE_RESPONSE	
			&& m_packetHeader.command[0] == NOT_CONTROL_COMMAND 
			&& m_packetHeader.command[1] == TRANSFER_FILE_HASH )
	{
		// Result 값 확인
		if( m_packetHeader.result[0] == HEADER_RESULT_SUCCESS )
		{
			bSuccess = true;
			return true;
		}
		else
		{
			// Hash 검사 실패인 경우 오류 메시지를 저장처리.
			bSuccess = false;
			if( GetPacketData( m_nPacketDataLen, szErrorString ) == true )
			{
				return true;
			}
			else
			{
				LOG( LERR, "Hash check result receive but error data receive fail");
			}
		}
	}
	else
	{
		// 다른 응답패킷인 경우
		LOG( LERR, "Hash check result fail by not TRANSFER_FILE_HASH response[%02x][%02x]" , m_packetHeader.command[0], m_packetHeader.command[1]);
	}

	return false;
}

/// @brief Alive Check 를 위한 패킷 전송 => 해당 패킷의 응답은 필요없으므로 수신시 무시할 것.
/// @return 전송 성공시 true, 실패시 false 반환.
bool CSocketControl::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 Control 로 부터 CONTROL_FILE_REPLICATION 또는 CONTROL_FILE_CACHE Request 요청을 받은 경우 \n
///<       Packet Data 부분의 상세 정보를 가져온다.
bool CSocketControl::GetFileRequestInfo( std::string& szFileName, unsigned long long& nFileSize, std::string& szTargetTranId,
		int& nFileHashCheckLevel, bool& bUseInternalIp, std::vector< std::string >& vecTargetFhs)
{
	if( IsValidSocket() == false )
		return false;

	unsigned int nLength;

	// Source File Name
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "File name size get failed.");		
		return false;
	}
	nLength = GetDataToUInt( m_tempBuffer, true);
	GetPacketData( nLength, szFileName );

	// File Size
	if( GetPacketData( 8 ) == false )
	{
		LOG( LERR, "File[%s] size packet get failed.", szFileName.c_str() );		
		return false;
	}
	nFileSize = GetDataToUInt64( m_tempBuffer );

	// ADD 2010-05-07 huibong
	// RC 간 동기화시 Target Tran ID 정보를 수신한다.
	// 만약 존재하지 않는 경우는 사용하지 않는다.
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "Target Tran ID size get failed.[%s]", szFileName.c_str() );		
		return false;
	}

	nLength = GetDataToUInt( m_tempBuffer, true );
	// Target Tran ID 정보는 존재하지 않을 수도 있으므로
	if( nLength > 0 )
		GetPacketData( nLength, szTargetTranId );
	else
		szTargetTranId.clear();

	// File hash check level	
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "File[%s] hash level get failed.", szFileName.c_str() );		
		return false;
	}
	nFileHashCheckLevel = GetDataToUInt( m_tempBuffer, true);

	// Use Internal IP
	if( GetPacketData( 1 ) == false )
	{
		LOG( LERR, "Use internal ip get failed.[%s]", szFileName.c_str());		
		return false;
	}

	if( m_tempBuffer[0] == 0x01 )
		bUseInternalIp = true;
	else
		bUseInternalIp = false;

	// FHS 목록 정보	
	unsigned int nCount;
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "Target FHS count get failed.[%s]", szFileName.c_str());		
		return false;
	}
	nCount = GetDataToUInt( m_tempBuffer, true);
	
	std::string szValue;
	for( unsigned int i = 0; i < nCount; i++)
	{
		szValue.clear();

		GetPacketData( 4 );
		nLength = GetDataToUInt( m_tempBuffer, true);		
		GetPacketData( nLength, szValue );

		vecTargetFhs.push_back( szValue );
	}

	return true;	
}

/// @brief Control 로 부터 CONTROL_FILE_CHECK Request 요청을 받은 경우 Packet Data의 상세 정보를 가져온다.
bool CSocketControl::GetRequestCheckFromControl( std::string& szFileName, bool& bHashCheck,  unsigned long long& nHashCheckSize )
{
	if( IsValidSocket() == false )
		return false;

	unsigned int nLength;

	// File Name
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "File name size get failed.");		
		return false;
	}
	nLength = GetDataToUInt( m_tempBuffer, true);
	GetPacketData( nLength, szFileName );

	// File Hash Check 여부. ( 0 인 경우 추출 안함... 1 인 경우 추출 )
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "File[%s] Hash check YN get failed.", szFileName.c_str() );		
		return false;
	}

	if( GetDataToUInt( m_tempBuffer, true) == 1 )
	{
		bHashCheck = true;

		// Hash 추출을 할 경우.. Hash 값을 추출할 Size 설정값을 가져온다.
		if( GetPacketData( 8 ) == false )
		{
			LOG( LERR, "File[%s] Hash extract size get failed.", szFileName.c_str() );		
			return false;
		}
		nHashCheckSize = GetDataToUInt64( m_tempBuffer );
	}
	else
	{
		bHashCheck = false;
		nHashCheckSize = 0;
	}
	
	return true;
}

// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460)
/// @brief Control 로 부터 CONTROL_FILE_CHECK_TOTO Request 요청을 받은 경우 Packet Data의 상세 정보를 가져온다.
bool CSocketControl::GetRequestCheckTOTOFromControl( std::string& szFileName )
{
	if( IsValidSocket() == false )
		return false;

	unsigned int nLength;

	// File Name
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "File name size get failed." );
		return false;
	}
	nLength = GetDataToUInt( m_tempBuffer, true );
	GetPacketData( nLength, szFileName );
	
	return true;
}


/// @brief Control 로 부터 COCONTROL_FILE_UNLINK Request 요청을 받은 경우 Packet Data의 상세 정보를 가져온다.
bool CSocketControl::GetRequestUnlinkFromControl( std::string& szFileName, unsigned long long& nFileSize, unsigned int& nUnlinkMode )
{
	if( IsValidSocket() == false )
		return false;

	unsigned int nLength;

	// File Name
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "File name size get failed.");		
		return false;
	}
	nLength = GetDataToUInt( m_tempBuffer, true);
	GetPacketData( nLength, szFileName );

	// File Size
	if( GetPacketData( 8 ) == false )
	{
		LOG( LERR, "File[%s] size packet get failed.", szFileName.c_str() );		
		return false;
	}
	nFileSize = GetDataToUInt64( m_tempBuffer );

	// Unlink Mode
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "Unlink mode get failed.");		
		return false;
	}
	nUnlinkMode = GetDataToUInt( m_tempBuffer, true);

	return true;
}

/// @brief Control 로 부터 CONTROL_FILE_STAT Request 요청을 받은 경우 Packet Data의 상세 정보를 가져온다.
bool CSocketControl::GetRequestStatFromControl( std::string & szFileName )
{
	if( IsValidSocket() == false )
		return false;

	unsigned int nLength;

	// File Name
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "File name size get failed." );
		return false;
	}
	nLength = GetDataToUInt( m_tempBuffer, true );
	GetPacketData( nLength, szFileName );

	return true;
}


/// @brief Control 로 각 요청에 대한 오류 메시지를 전송한다. 
bool CSocketControl::SendErrorMessageToControl( const std::string& szErrorMessage, char commandCode )
{
	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_RESPONSE;
	stHeader.command[0]		= commandCode;
	stHeader.result[0]		= HEADER_RESULT_ERROR;
	stHeader.data_length	= htonl( szErrorMessage.size());
	
	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "Error result message send fail to control [%s]", szErrorMessage.c_str() );		
		return false;
	}
	else
	{
		WriteN( szErrorMessage.c_str(), szErrorMessage.size());
	}
	
	return true;
}

/// @brief Control 로 파일 복제 요청에 대한 성공 처리 결과 메시지를 전송한다. 
bool CSocketControl::SendFileReplicationResult( char commandCode
		, std::map< std::string, std::string>& mapSuccessList, std::map< std::string, std::string>& mapFailList )
{
	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_RESPONSE;
	stHeader.command[0]		= commandCode;
	stHeader.result[0]		= HEADER_RESULT_SUCCESS;
	
	// 전체 크기 계산.
	unsigned int nTemp = 0;
	std::map< std::string, std::string>::const_iterator it;

	nTemp += 4; // Success Count
	for( it = mapSuccessList.begin(); it != mapSuccessList.end(); it++)
	{
		nTemp += 4;	// Host Legnth
		nTemp += it->first.size(); // Host Name

		nTemp += 4;	// File Name Legnth
		nTemp += it->second.size(); // File Name
	}

	nTemp += 4; // Faile Count
	for( it = mapFailList.begin(); it != mapFailList.end(); it++)
	{
		nTemp += 4;	// Host Legnth
		nTemp += it->first.size(); // Host Name

		nTemp += 4;	// Error Legnth
		nTemp += it->second.size(); // Error Message
	}

	stHeader.data_length	= htonl( nTemp );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "File replication result send fail to control.");		
		return false;
	}
	else
	{
		// Data 부분을 전송한다.

		// 1.Success Count
		nTemp = htonl( mapSuccessList.size() );	
		WriteN( &nTemp, 4);
		for( it = mapSuccessList.begin(); it != mapSuccessList.end(); it++)
		{
			nTemp = htonl( it->first.size() );
			WriteN( &nTemp, 4);
			WriteN( it->first.c_str(), it->first.size());

			nTemp = htonl( it->second.size() );
			WriteN( &nTemp, 4);
			WriteN( it->second.c_str(), it->second.size());
		}
		
		// 2.Fail Count
		nTemp = htonl( mapFailList.size() );	
		WriteN( &nTemp, 4);
		for( it = mapFailList.begin(); it != mapFailList.end(); it++)
		{
			nTemp = htonl( it->first.size() );
			WriteN( &nTemp, 4);
			WriteN( it->first.c_str(), it->first.size());

			nTemp = htonl( it->second.size() );
			WriteN( &nTemp, 4);
			WriteN( it->second.c_str(), it->second.size());
		}
	}
	
	return true;
}

/// @brief Control 로 Check 요청에 대한 성공 처리 결과를 전달한다.
bool CSocketControl::SendCheckResultToControl( char commandCode, const unsigned long long& nFileSize, const std::string& szHashValue )
{
	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_RESPONSE;
	stHeader.command[0]		= commandCode;
	stHeader.result[0]		= HEADER_RESULT_SUCCESS;

	// 전체 크기 계산.
	unsigned int nTemp = 0;

	nTemp += 8 ; // File Size ( 8 Byte )
	nTemp += 4 ; // Hash Value Length Length;
	nTemp += szHashValue.size();

	stHeader.data_length	= htonl( nTemp );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "Check result send fail to control" );		
		return false;
	}
	else
	{
		// Data 부분 전송

		// 1. File Size 전송
		WriteN( &nFileSize, 8 );

		// 2. Length ( Hash 필드 )
		nTemp = htonl( szHashValue.size() );
		WriteN( &nTemp, 4);

		// 3. Hash 추출값.. ( 존재할 때만 전송 )
		if( szHashValue.size() > 0 )
			WriteN( szHashValue.c_str(), szHashValue.size() );
	}

	return true;
}


// NEW 2016-05-11 huibong 토토디스크 지원용 기능 추가 (#27460, #27533)
/// @brief 고객사 토토디스크 지원을 위한 기능
///        Control 로 부터 전달받은 Content 에 대해 Size 정보 및 MD5 Hash, Checksum 정보 등을 추출하여 전달한다.
bool CSocketControl::SendTOTOCheckResultToControl( char commandCode, const unsigned long long& nFileSize
	                                             , const std::string& szResultTotoMd5, const std::string& szResultChecksum, const std::string& szResultDnaCheckKey )
{
	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_RESPONSE;
	stHeader.command[0] = commandCode;
	stHeader.result[0]  = HEADER_RESULT_SUCCESS;

	// 전체 크기 계산.
	unsigned int nTemp = 0;

	nTemp += 8; // File Size ( 8 Byte )
	nTemp += 4; // szResultTotoMd5 Length;
	nTemp += szResultTotoMd5.size();
	nTemp += 4; // szResultChecksum Length;
	nTemp += szResultChecksum.size();
	nTemp += 4; // szResultDnaCheckKey Length;
	nTemp += szResultDnaCheckKey.size();
	

	stHeader.data_length = htonl( nTemp );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "Check result send fail to control" );
		return false;
	}
	else
	{
		// Data 부분 전송

		// 1. File Size 전송
		WriteN( &nFileSize, 8 );

		// 2. szResultTotoMd5 항목 전송
		nTemp = htonl( szResultTotoMd5.size() );
		WriteN( &nTemp, 4 );
		// 존재할 때만 전송
		if( szResultTotoMd5.size() > 0 )
			WriteN( szResultTotoMd5.c_str(), szResultTotoMd5.size() );
		
		// 3. szResultChecksum 항목 전송
		nTemp = htonl( szResultChecksum.size() );
		WriteN( &nTemp, 4 );
		// 존재할 때만 전송
		if( szResultChecksum.size() > 0 )
			WriteN( szResultChecksum.c_str(), szResultChecksum.size() );


		// 4. szResultTotoMd5 항목 전송
		nTemp = htonl( szResultDnaCheckKey.size() );
		WriteN( &nTemp, 4 );
		// 존재할 때만 전송
		if( szResultDnaCheckKey.size() > 0 )
			WriteN( szResultDnaCheckKey.c_str(), szResultDnaCheckKey.size() );		
	}

	return true;
}

bool CSocketControl::SendStatResultToControl( char commandCode, const long & nFileSize, const long & nAccessTime, const long & nModifyTime, const long & nChangeTime )
{
	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_RESPONSE;
	stHeader.command[0] = commandCode;
	stHeader.result[0] = HEADER_RESULT_SUCCESS;

	// 전체 크기 계산.
	unsigned int nTemp = 0;

	nTemp += 8; // File Size ( 8 Byte )
	nTemp += 8; // access time ( 8 Byte )
	nTemp += 8; // modify time ( 8 Byte )
	nTemp += 8; // change time ( 8 Byte )

	stHeader.data_length = htonl( nTemp );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "Stat result send fail to control" );
		return false;
	}
	else
	{
		// Data 부분 전송

		// File Size 전송
		WriteN( &nFileSize, 8 );

		// access, modify, change time 정보 전송
		WriteN( &nAccessTime, 8 );
		WriteN( &nModifyTime, 8 );
		WriteN( &nChangeTime, 8 );
	}

	return true;
}


/// @brief Control 로 Unlink 요청에 대한 성공 처리 결과를 전달한다... Data 부분은 존재하지 않음.
bool CSocketControl::SendUnlinkResultToControl( char commandCode )
{
	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_RESPONSE;
	stHeader.command[0]		= commandCode;
	stHeader.result[0]		= HEADER_RESULT_SUCCESS;
	stHeader.data_length	= htonl( 0 );			// unlink 성공시 전달되는 Data 부분이 존재하지 않음.

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "Unlink result send fail to control" );		
		return false;
	}
	
	return true;
}

///////////////////////////////////////////////////////////////////////////////////////

// CHG 2012-02-02 huibong
// 잘못된 Packet을 전달 받았을 경우 해당 결과를 반환하여.. Session 의 유지 여부를 판단토록 수정 처리
// 특히 잘못된 Packet Header 가 수신될 경우 이에 대한 방어 코드가 존재하지 않아..
// 지속된 Packet Header 수신 대기로 인해 CPU, Memory 점유율 등의 문제가 발생
// 이를 해결하기 GetPacketHeader() 함수의 반환값을 다양화하여 각 호출함수에서 Connection 에 대한 처리 여부를 판단토록 한다.

/// @brief Packet Header 부분의 수신 처리를 위한 함수.
/// @param timeout [in] 대기시간.
/// @retrun 
///         1 : PACKET_HEADER_RECEIVE_SUCCESS          : 성공시           
///         0 : PACKET_HEADER_RECEIVE_TIMEOUT          : Timeout 발생시
///        -1 : PACKET_HEADER_RECEIVE_SOCK_CLOSED      : socket 종료된 경우
///        -2 : PACKET_HEADER_RECEIVE_INVALID_PROTOCOL : 잘못된 Packet 형식인 경우
///        -3 : PACKET_HEADER_RECEIVE_READ_ERROR       : 기타 오류

int CSocketControl::GetPacketHeader( int timeout )
{
	if( IsValidSocket() == false )
	{
		LOG( LERR, "IsValidSocket return false.");

		// 유효하지 않은 socket 이므로 -1 을 반환
		// return false;
		return PACKET_HEADER_RECEIVE_SOCK_CLOSED;
	}

	int nRead;
	bool isAlivePacket;
	do
	{
		isAlivePacket = false;

		// Socket 으로 부터 Packet Header 부분 수신.
		// 수신된 정보는 멤버변수에 저장처리.
		// ReadNTimeout() 반환값은 read 된 데이터의 크기. 0: fd closed, -1: 오류 , -2: Timeout
		nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, timeout );

		// 정상적으로 Data 를 수신한 경우
		if( nRead > 0 )
		{
			// Packet 형식 코드 중 STX code 검사.
			if( m_packetHeader.stx != HEADER_STX_CODE )
			{
				LOG( LERR, "Not valid STX code ");
				PrintHeaderToLog();

				// 잘못된 Packet 형식인 경우 -2 값을 반환
				//return false;
				return PACKET_HEADER_RECEIVE_INVALID_PROTOCOL;
			}

			// 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;
			}

		}
		else
		{
			// Socket 이 종료된 경우
			if( nRead == 0 )
			{				
				//LOG( LINF, "Connection already closed.");
				Close();	// 확실하게 종료 처리 한 후

				// Connection 이 종료된 경우 -1 을 반환
				return PACKET_HEADER_RECEIVE_SOCK_CLOSED;
			}
			else if( nRead == -1 )	// read 함수 관련 오류가 발생한 경우
			{
				LOG( LERR, "Socket read() function error. Connection closed by forced.");				
				Close();	// 확실하게 종료 처리 한 후

				// 오류 발생시 -3 을 반환
				return PACKET_HEADER_RECEIVE_READ_ERROR;

			}
			else if( nRead == -2 )	// Timeout 이 발생한 경우
			{
				LOG( LDBG, "Socket read() timeout [%d]", timeout);

				// Timeout 발생시에는 0 을 반환
				return PACKET_HEADER_RECEIVE_TIMEOUT;
			}
		}

	} while( isAlivePacket == true );


	return PACKET_HEADER_RECEIVE_SUCCESS;

}

/// @brief socket 에서 지정된 크기만큼의 데이터를 읽어 출력변수에 저장처리.
/// @param size [in] read 할 데이터 크기
/// @param value [out] 읽은 데이터를 저장할 string 변수
/// @return On success return true, otherwise return false.
bool CSocketControl::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 );
	
	// 오류 발생시
	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 CSocketControl::GetPacketData( unsigned int size )
{
	// 1. 수신할 크기가 임시버퍼보다 큰 경우 오류 반환.
	if( size > DEFAULT_SOCKET_TEMP_BUFFER_SIZE )
		return false;

	// 2. 데이터 수신 처리.
	int nRead = ReadNTimeout( m_tempBuffer, size );
	
	// 오류 발생시
	if( nRead <= 0 )
		return false;

	// 3. 정상인 경우는 ReadNTimeout 함수가 무조건 요청 크기만큼 read 처리하므로 정상수신임.
	return true;
}

/// @brief 전달받은 데이터를 unsigned int 형으로 변환처리 및 Endian 변환
unsigned int CSocketControl::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 CSocketControl::GetDataToUInt64( BYTE * pValue )
{
	unsigned long long * pResult;
	BYTE tempBuffer[8];
	memcpy(tempBuffer, pValue, 8);

	pResult = (unsigned long long *)tempBuffer;

	return  *pResult;
}

/// @brief 전달받은 Directory 정보를 확인하여 존재하지 않을 경우 생성처리.
bool CSocketControl::MakeDirectory( const std::string& szFullPath )
{
	if( szFullPath.size() <= 0 )
		return false;

	struct stat dirStat;

	// 디렉토리 정보 검사.
	// 해당 이름을 가진 Directory 또는 파일이 존재하고
	if( lstat ( szFullPath.c_str(), &dirStat ) == 0)
	{
		// 해당 정보가 Directory 인 경우
		if( S_ISDIR( dirStat.st_mode ) != 0 )
			return true;
	}

	// 해당 디렉토리가 존재하지 않는 경우 무조건 생성 처리.
	
	// CHG 2014-11-03 huibong
	// - mkdir() function use
	// - chown() add for nobody:nobody

	//char tempBuffer[1024];		
	//sprintf( tempBuffer, "/bin/mkdir -p -m0777 %s", szFullPath.c_str() );
	//system(tempBuffer);

	// Directory 생성
	if( mkdir( szFullPath.c_str(), 0755) == 0 )
    {
        // chown nobody:nobody 권한 변경 
		if( chown( szFullPath.c_str(), 65534, 65534 ) == 0 )
		{
			_LOG( LINF, "Directory create and chown to nobody OK. [%s]", szFullPath.c_str());
			return true;
		}
		else
		{
			int errorNum = errno;
			LOG( LERR, "Directory created. but chown() to nobody faild. [%s][%d][%s]", szFullPath.c_str(), errorNum, strerror(errorNum));

	        return false;
		}
    }
    else
    {
        int errorNum = errno;

        LOG( LERR, "Directory create faild. [%s][%d][%s]", szFullPath.c_str(), errorNum, strerror(errorNum));
        return false;
    }
}


/// @brief Socket 으로 부터 수신받은 데이터를 파일에 Write 처리한다.
///< 만약 File Write 처리시 오류가 발생하는 경우 Socket Data 의 정상적인 처리를 위해
///< Socket 데이터 수신만 처리하고 오류를 반환한다.
/// @returnFile Write 가 오류 없이 정상적으로 처리된 경우 true, 오류발생시 false 및 오류메시지 반환
bool CSocketControl::ReceiveToFile( FILE * pFile, unsigned long long nFileSize, std::string& errorMessage )
{
	if( IsValidSocket() == false )
		return false;
	
	// 0 Byte 크기의 파일인 경우 그냥 SUCCESS 처리.
	if( nFileSize == 0 )
		return true;

	char tempBuffer[FILE_DATA_BUFFER_SIZE];
	int onceReadSize;
	int nRead;
	unsigned long long nLeft = nFileSize;
	bool bFileWriteSuccess = true;

	int nLockResult;


	// NEW 2012-05-16 huibong
	// 동일 파일에 대한 동시 Write 처리를 방어하기 위해 file lock 개념을 추가한다.
	int fd = fileno( pFile );
	nLockResult = lockf( fd, F_TLOCK, 0 );	// 파일 전체에 대해 lock 시도.

	if( nLockResult == -1 )
	{
		int errorNum = errno;
		if( errorNum == EAGAIN )
		{
			// 이미 Lock 이 걸려 있는 상태인 경우... 5 sec 후 재시도 처리..
			// 만약 재시도가 실패하는 경우.. 실패로 반환.
			LOG( LWAR, "File lock 1th fail. 5 sec sleep and retry [%d][%s]", errorNum, strerror(errorNum));

			sleep(5);
			nLockResult = lockf( fd, F_TLOCK, 0 );	// 파일 전체에 대해 lock 재시도.

			if( nLockResult == -1 )
			{
				errorNum = errno;

				// Lock 재시도 실패시.. 오류 반환.
				LOG( LERR, "File lock fail. file receive fail [%d][%s]", errorNum, strerror(errorNum));

				errorMessage = "File data receive error [file lock fail]";
				return false;
			}
		}
		else
		{
			// 이미 Lock 상태로 인한 lock 처리 실패가 아닌 경우.
			LOG( LERR, "File lock fail by system error. file receive fail [%d][%s]", errorNum, strerror(errorNum));
			errorMessage = "File data receive error [file lock fail]";
			return false;
		}
	}


	while( nLeft > 0 )
	{
		// 한번에 읽을 용량 계산.
		if( nLeft > FILE_DATA_BUFFER_SIZE )
			onceReadSize = FILE_DATA_BUFFER_SIZE;
		else
			onceReadSize = nLeft;

		// Socket Read
		if( (nRead = read( m_sock, tempBuffer, onceReadSize )) < 0 )
		{
			int errorNum = errno;
			if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK )
			{
				nRead = 0;
				continue;
			}
			else if( errorNum == ECONNRESET )
			{
				m_bConnected = false;
				errorMessage = "File receive error [connection closed].";

				lockf( fd, F_ULOCK, 0 );
				return false;
			}
			else
			{
				errorMessage = "File data receive error [socket read error]";
				LOG( LERR, "Socket read error.[%d][%s]", errorNum, strerror(errorNum));

				lockf( fd, F_ULOCK, 0 );
				return false;
			}
		}
		else if( nRead == 0 )  // Connection Closed..
		{
			m_bConnected = false;
			errorMessage = "File receive error [connection closed].";

			lockf( fd, F_ULOCK, 0 );
			return false;
		}
		
		// 정상적인 경우.
		// File Write 시도
		if( bFileWriteSuccess == true )
		{
			if( (int)fwrite( tempBuffer, 1, nRead, pFile ) != nRead )
			{
				// file write 실패시.
				int errorNum = errno;
				LOG( LERR, "File write to local error.[%d][%s]", errorNum, strerror(errorNum));

				// TODO 소켓을 읽어 전송받은 데이터를 날려버려야 할까요..?
				errorMessage = "File write failed from receive data";
				bFileWriteSuccess = false;
			}
		}

		nLeft -= nRead;
	}

	// file Lock 해제 처리.
	lockf( fd, F_ULOCK, 0 );

	// File Write 가 오류 없이 정상적으로 처리된 경우 true
	// 오류발생시 false 반환처리.
	if( bFileWriteSuccess == true )
		return true;
	else
		return false;
}

// Socket 으로 부터 수신받은 데이터를 읽어서.. 버린다.
// - 기존 물리 파일이 존재하는 경우.. overwrite 처리를 않기로 해서...
// - socket 으로 부터 수신된 data 는 버린다.
// return 오류 없이 정상적으로 처리된 경우 true, 오류발생시 false 및 오류메시지 반환
bool CSocketControl::ReceiveToDummy( unsigned long long nFileSize, std::string& errorMessage )
{
	if( IsValidSocket() == false )
		return false;

	// 0 Byte 크기의 파일인 경우 그냥 SUCCESS 처리.
	if( nFileSize == 0 )
		return true;
	
	char tempBuffer[FILE_DATA_BUFFER_SIZE];
	int onceReadSize;
	int nRead;
	unsigned long long nLeft = nFileSize;
		
	while( nLeft > 0 )
	{
		// 한번에 읽을 용량 계산.
		if( nLeft > FILE_DATA_BUFFER_SIZE )
			onceReadSize = FILE_DATA_BUFFER_SIZE;
		else
			onceReadSize = nLeft;

		// Socket Read
		if( ( nRead = read( m_sock, tempBuffer, onceReadSize ) ) < 0 )
		{
			int errorNum = errno;
			if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK )
			{
				nRead = 0;
				continue;
			}
			else if( errorNum == ECONNRESET )
			{
				m_bConnected = false;
				errorMessage = "File receive error [connection closed].";

				return false;
			}
			else
			{
				errorMessage = "File data receive error [socket read error]";
				LOG( LERR, "Socket read error.[%d][%s]", errorNum, strerror( errorNum ) );
								
				return false;
			}
		}
		else if( nRead == 0 )  // Connection Closed..
		{
			m_bConnected = false;
			errorMessage = "File receive error [connection closed].";
						
			return false;
		}


		nLeft -= nRead;
	}

	
	return true;	
}
