#include "WorkerSizeHash.h"

#include "Logger.h"
#include "ProcessRename.h"
#include "ProcessConfig.h"

#include "SocketApiClient.h"
#include "ServiceInfo.h"
#include "ContentSelect.h"
#include "../ftsd/FtsdSocketControl.h"


#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/syscall.h>


// client ip 주소 저장을 위한 최대 크기값
// - NULL 포함 40자까지, IPv6 지원 목적
#define IP_ADDR_LENGTH      40


// client 요청 수신 관련 tiemout 최대값
#define MAX_REQUEST_TIMEOUT			15	


// 고객사에서 요청하는 Content size, md5 hash 정보 처리를 위한 TCP listen socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_listenSocketSizeHash;

// ServiceInfo 전역객체
// - service 정보를 caching 하는 객체는 각 client thread 에서 사용해야 하므로.. 전역 객체로 생성한다.
CServiceInfo g_objServiceInfo;


// 각 Client 를 처리할 Thread 에 전달할 구조체 
struct stSizeHashThreadArg {

	// client socket descriptor
	int clientSocket;

	// Client 의 IP 주소 정보를 저장한 변수	
	char szClientAddress[IP_ADDR_LENGTH];
};


// thread id 정보 반환 함수.
long GetThreadId()
{
	//return syscall( __NR_gettid );
	return syscall( SYS_gettid );
}


// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerSizeHash()
{
	// open 된 socket 관련 Close 처리
	if( g_listenSocketSizeHash != -1 )
		close( g_listenSocketSizeHash );

	// Logger 객체 종료 처리.
	CLogger::Exit();

	return;
}

// Worker SizeHash 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerSizeHashTerminate( int nSignalNumber )
{
	// Signal Number 에 따른 로깅처리.
	if( nSignalNumber == SIGTERM )
	{
		_LOG( LINF, "[SizeHash %d] Process exit by SIGTERM signal. Good Bye..", getpid() );
	}
	else
	{
		_LOG( LWAR, "[SizeHash %d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
	}

	ReadyToExitWorkerSizeHash();

	// 종료전 잠시 대기
	struct timespec sleep;
	sleep.tv_sec = 0;
	sleep.tv_nsec = 500000000;  // 0.5 sec
	nanosleep( &sleep, NULL );

	exit( EXIT_SUCCESS );
}

// Worker::SizeHash Process 의 signal 처리기...
void SetWorkerSizeHashSignalHandler()
{
	sigset_t set;
	struct sigaction act;

	memset( &act, 0x00, sizeof( act ) );
	sigfillset( &set );
	sigprocmask( SIG_SETMASK, &set, NULL );
	sigfillset( &act.sa_mask );

	// 무시 처리 signal
	act.sa_handler = SIG_IGN;
	sigaction( SIGPIPE, &act, NULL );	/* desciptor 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
	sigaction( SIGHUP, &act, NULL );	/* process를 기동시킨 관리자의 로그아웃시, 또는 config reload 등의 처리 signal*/
	sigaction( SIGINT, &act, NULL );	/* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
	sigaction( SIGQUIT, &act, NULL );	/* 키보드에 의한  Abort 신호 처리 => ? */

	// Worker 프로세스는 자식 프로세스가 존재하지 않으므로 그냥 무시처리함.
	act.sa_handler = SIG_IGN;
	sigaction( SIGCHLD, &act, NULL );

	/* 각종 에러나 사용자의 종료 신호 처리 */
	act.sa_handler = SignalWorkerSizeHashTerminate;
	sigaction( SIGTERM, &act, NULL );	/* kill -TERM 에 의한 프로세스 종료시 */

	// 나머지 신호는 default 처리.

	sigemptyset( &set );                  /* 신호 처리기 처리 설정 위한 블록 해제 */
	sigprocmask( SIG_SETMASK, &set, NULL );
}

// Worker::SizeHash 에서 각 Client 에 대한 처리를 수행하기 위한 Thread.
void * WorkerSizeHashClientThread( void * arg )
{
	// 전달받은 변수를 내부 변수에 저장 처리.

	// size, hash 요청 관련 client socket 처리를 담당할 객체 생성	
	CSocketApiClient clientSocket( ( (struct stSizeHashThreadArg *)arg )->clientSocket );

	// client 주소 처리 관련 
	char szClientAddress[IP_ADDR_LENGTH];
	memcpy( szClientAddress, ( (struct stSizeHashThreadArg *)arg )->szClientAddress, IP_ADDR_LENGTH );

	// malloc 로 생성된 변수 해제 처리
	free( arg );

	// 본 thead 는 client 연결 상태에 따라 종료되므로.. process 에서 분리 처리
	pthread_detach( pthread_self() );

	_LOG( LINF, "[SizeHash %d %ld] client thread start. [%s]", getpid(), GetThreadId(), szClientAddress );

	// 사용 변수 초기화
	int nResult;

	// 2021-03-12 세션 종료 관련 API Server 담당 이준기 과장 협의 내역
	// - API Server 에서 request 에 대한 결과 수신 완료 후 세션 종료 처리한다고 함.
	// - 따라서... 본 모듈은 Server 이므로... 먼저 세션 clsoe 처리하지 않고....
	// - 1개 세션에 대해서 다중 요청을 처리해 줄 수 있도록 한다.
	// - 만약 오류 발생 또는 통신규칙 위반시에는 바로 세션 종료 처리한다.

	// socket 수신 대기
	while( 1 )
	{

		// request 수신 대기
		nResult = clientSocket.GetRequestHeader( MAX_REQUEST_TIMEOUT );

		if( nResult == 0 )
		{
			// timeout 발생시...
			// - 지정된 시간동안 요청이 없는 경우 세션을 종료 처리한다.
			_LOG( LINF, "[SizeHash %d %ld] client[%s] request timeout[%d]. session close."
				, getpid(), GetThreadId(), szClientAddress, MAX_REQUEST_TIMEOUT );

			// 세션 종료 처리
			break;
		}
		else if( nResult < 0 )
		{
			// Socket 종료, 오류 또는 정의되지 않은 형식의 패킷 header 수신시...

			if( clientSocket.IsValidSocket() == false )
			{
				// socket 종료시
				_LOG( LINF, "[SizeHash %d %ld] client[%s] session closed by client."
					, getpid(), GetThreadId(), szClientAddress );

			}
			else
			{
				// socket 처리 오류 또는 정의되지 않은 요청 수신시
				_LOG( LERR, "[SizeHash %d %lu] client[%s] request recv error. session close."
					, getpid(), GetThreadId(), szClientAddress );
			}

			// 세션 종료 처리
			break;
		}


		// 정상 request header 수신시.
		std::string szService;
		std::string szUri;
		std::string szErrorMessage;

		// size, hash 정보 추출 요청 관련 body 정보 수신
		nResult = clientSocket.GetRequestSizHashBody( szService, szUri, szErrorMessage );

		if( nResult == -1 )
		{
			// body 정보 수신 실패시. (timeout, length 오류 등)
			_LOG( LERR, "[SizeHash %d %ld] client[%s] request body recv error[%s]. session close."
				, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );

			// client 에 응답 하지 않고.. 세션을 바로 종료 처리한다.
			break;
		}
		else if( nResult == 0 )
		{
			// body 정보가 지정된 규칙의 key 가 존재하지 않아 파싱 오류시
			_LOG( LERR, "[SizeHash %d %ld] client[%s] request body format error[%s]."
				, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );

			// 파싱 오류가 발생할 상황이므로... 우선 client 로 error message 전송을 시도한다.
			if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
			{
				_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s]"
					, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );

			}

			// request 관련 통신 규칙 문제이므로...
			// 세션을 다시 대기시키지 않고 약 1 sec sleep 후 정상 종료 처리 시킨다.
			sleep( 2 );
			break;
		}

	
		// body 정보 정상 수신시...

		_LOG( LINF, "[SizeHash %d %ld] client[%s] request recv ok. service[%s] uri[%s]"
			, getpid(), GetThreadId(), szClientAddress, szService.c_str(), szUri.c_str() );

		// 추출된 service , uri 정보가 유효한지 check
		if( szService.empty() == true || szUri.empty() == true )
		{
			szErrorMessage = "request info not valid. service[" + szService + "] uri[" + szUri + "]";

			_LOG( LERR, "[SizeHash %d %ld] client[%s] request info not valid. servcie[%s] uri[%s]"
				, getpid(), GetThreadId(), szClientAddress, szService.c_str(), szUri.c_str() );

			// client 한테 오류 내역 전송 처리 한다.
			if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
			{
				_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s]"
					, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
			}

			// 다시 request 대기
			continue;
		}


		// 결과를 저장할 임시 변수
		bool bUriIgnoreCase;
		std::string szServiceSeq;
	
		// service 에 해당하는 서비스 seq 정보를 가져온다.
		if( g_objServiceInfo.GetServiceInfo( szService, szServiceSeq, bUriIgnoreCase ) == false )
		{
			szErrorMessage = "request service[" + szService + "] info not found.";

			_LOG( LERR, "[SizeHash %d %ld] client[%s] request servcie[%s] info not found."
				, getpid(), GetThreadId(), szClientAddress, szService.c_str() );

			// client 한테 오류 내역 전송 처리 한다.
			if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
			{
				_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s]"
					, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
			}

			// 다시 request 대기
			continue;
		}


		// 서비스 정보가 정상적으로 존재하는 경우...
		// RCDB 에서 해당 서비스의 uri 정보를 조회하여.. 물리 파일 정보를  확인한다.
		CContentSelect objContentSelect;
		if( objContentSelect.Start() == false )
		{
			szErrorMessage = "db connection failed.";

			_LOG( LERR, "[SizeHash %d %ld] client[%s] db select module start failed. [%s]"
				, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );

			// client 한테 오류 내역 전송 처리 한다.
			if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
			{
				_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s]"
					, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
			}

			// DB 세션 종료 처리
			objContentSelect.Exit();

			// 다시 request 대기
			continue;
		}
		
		// RCDB 상에서 content 정보 조회 
		std::vector<struct HostSizeFilenameInfo> vecContentInfo;
		vecContentInfo.clear();

		if( objContentSelect.GetHostSizeFilename( szServiceSeq, bUriIgnoreCase, szUri, szErrorMessage, vecContentInfo ) == false )
		{
			_LOG( LERR, "[SizeHash %d %ld] client[%s] rcdb select fail. [%s][%s]"
				, getpid(), GetThreadId(), szClientAddress
				, szUri.c_str(), szErrorMessage.c_str() );

			// client 한테 오류 내역 전송 처리 한다.
			if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
			{
				_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s]"
					, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
			}

			// DB 세션 종료 처리
			objContentSelect.Exit();

			// 다시 request 대기
			continue;
		}

		// RCDB 세션 종료 처리
		objContentSelect.Exit();


		// DB 정보 조회 완료
		_LOG( LDBG, "[SizeHash %d %ld] client[%s] rcdb select ok. [%s] row_count[%lu]"
			, getpid(), GetThreadId(), szClientAddress
			, szUri.c_str(), vecContentInfo.size() );


		// 해당 FHS 의 ftsd 로 hash check 요청을 전송하고... 결과를 대기한다.

		// FHS ftsd 와 통신 처리 담당한 socket 객체 생성
		CFtsdSocketControl fhs;
		
		unsigned long long uContentLength = 0;
		std::string szHash;
		szHash.clear();

		// Size, MD5 정보 추출 성공 여부
		bool bExtractSuccess = false;

		// 조회된 FHS 로 content 정보 추출 명령 전달.
		// - 조회 list 는 origin - replication 순이며...
		// - origin 접속 실패시.. replication 에서 추출 처리한다.
		std::vector<struct HostSizeFilenameInfo>::const_iterator it;
		for( it = vecContentInfo.begin(); it != vecContentInfo.end(); it++ )
		{
			if( fhs.ConnectTarget( it->szHost, ( CProcessConfig::GetInstance()->GetFhsTransferDaemonPort() ) ) == false )
			{
				_LOG( LERR, "[SizeHash %d %ld] client[%s] fhs connect fail. [%s][%s]" 
					, getpid(), GetThreadId(), szClientAddress
					, it->szHost.c_str(), szUri.c_str() );


				// 다음 FHS 에서 재시도
				fhs.Close();
				continue;
			}

			// FHS 와 정상 접속시...
			// - 전체 크기에 대한 hash 추출 요청 전달.
			if( fhs.SendFileCheckRequest( it->szFilename, true, 0 ) == false )
			{
				// 해당 FHS 로 요청 전달 실패시..
				_LOG( LERR, "[SizeHash %d %ld] client[%s] fhs request send fail. [%s][%s][%s]" 
					, getpid(), GetThreadId(), szClientAddress
					, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str() );

				// 다음 FHS 에서 재시도
				fhs.Close();
				continue;
			}

			_LOG( LINF, "[SizeHash %d %ld] client[%s] fhs hash request send ok. [%s][%s][%s]"
				, getpid(), GetThreadId(), szClientAddress
				, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str() );


			// 사용 변수 초기화

			int nTimeout = 5;			// 응답 대기 시간 ( sec )
			bool bJobSuccess = false;	// 처리 결과의 성공/실패 여부를 저장하기 위한 변수.

			// fhs 로 부터 응답 수신 대기
			while( 1 )
			{
				// nTimeout 에 지정된 시간동안 응답대기 시도
				// GetFileCheckResult() 함수는 SocketControl.h 파일 참고.
				nResult = fhs.GetFileCheckResult( nTimeout, bJobSuccess, szErrorMessage, uContentLength, szHash );

				if( nResult == -1 )
				{
					// Socket 통신 관련 오류 또는 접속 종료가 발생한 경우.
					// 해당 내역 로깅 및 루프 종료
					_LOG( LERR, "[SizeHash %d %ld] client[%s] fhs response wait fail by socket. [%s][%s][%s]"
						, getpid(), GetThreadId(), szClientAddress
						, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str() );

					// 다음 FHS 에서 재시도
					fhs.Close();
					break;
				}
				else if( nResult == 0 )
				{
					// 지정된 시간 동안 응답대기 중 처리 결과 정보가 아직 수신되지 않은 경우 => Allive Check 패킷 한번 쏘고 다시 Loop 로
					if( fhs.SendAliveCheck() == false )
					{
						// Alive 전송 실패시 => Socket 종료 및 오류가 발생한 경우임.
						_LOG( LERR, "[SizeHash %d %ld] client[%s] fhs response wait2 fail by socket. [%s][%s][%s]"
							, getpid(), GetThreadId(), szClientAddress
							, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str() );

						// 다음 FHS 에서 재시도
						fhs.Close();
						break;
					}

					// 정상적인 경우 다시 응답대기.
					continue;
				}
				else if( nResult == 1 )
				{
					// 요청에 대한 처리결과 정보가 수신된 경우. 
					if( bJobSuccess == true )
					{
						// 요청에 대한 처리가 정상적으로 처리된 경우
						_LOG( LINF, "[SizeHash %d %ld] client[%s] fhs extract ok. [%s][%s][%s]: [%llu][%s]"
							, getpid(), GetThreadId(), szClientAddress
							, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str()
							, uContentLength, szHash.c_str() );


						// 물리 파일 size 와 RCDB size 가 동일한지 check.
						if( uContentLength != it->uContentLength )
						{
							_LOG( LWAR, "[SizeHash %d %ld] client[%s] content size mismatch. [%s][%s][%s]: local[%llu] rcdb[%llu]"
								, getpid(), GetThreadId(), szClientAddress
								, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str()
								, uContentLength, it->uContentLength );
						}
						
						// 정상 응답을 받았으니.. 정상 처리 flag 설정
						bExtractSuccess = true;

					}
					else
					{
						// FHS 로 부터 오류 응답을 수신한 경우..
						_LOG( LERR, "[SizeHash %d %ld] client[%s] fhs extract error. [%s][%s][%s]: [%s]"
							, getpid(), GetThreadId(), szClientAddress
							, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str(), szErrorMessage.c_str() );
					}

					// 응답을 받았으니 응답 대기 루프 종료
					break; 
				}
				else
				{
					// Replication 요청에 대한 응답패킷이 아닌 경우.
					// 해당 패킷은 무시하고 다시 Loop 로 
					continue;
				}
			} // while(1)


			// 해당 FHS 에서 정상 응답 수신을 못한 경우.. 다음 장비에서 요청 처리한다.
			fhs.Close();
			if( bExtractSuccess == true )
			{
				// 정상 응답을 수신한 경우.. 다음 FHS 한테 시도하지 않고.. 종료 처리한다. 
				break;
			}
			else
			{
				// 정상 응답을 수신하지 못한 경우.. 다음 장비로 다시 시도.
				continue;
			}

		} // for()


		// 사용 완료된 변수는 정리
		vecContentInfo.clear();


		// 최종.. API Client 로 결과 전송 처리
		if( bExtractSuccess == true )
		{
			// client 한테 최종 결과 내역 전송 처리 한다.
			if( clientSocket.SendResultSizeHash( szService, szUri, uContentLength, szHash ) == false )
			{
				_LOG( LERR, "[SizeHash %d %ld] client[%s] response send fail. [%s][%s]: size[%llu] hash[%s]"
					, getpid(), GetThreadId(), szClientAddress
					, szService.c_str(), szUri.c_str()
					, uContentLength, szHash.c_str() );
			}
			else
			{
				_LOG( LINF, "[SizeHash %d %ld] client[%s] response send ok. [%s][%s]: size[%llu] hash[%s]"
					, getpid(), GetThreadId(), szClientAddress
					, szService.c_str(), szUri.c_str()
					, uContentLength, szHash.c_str() );
			}
		}
		else
		{
			// client 한테 오류 내역 전송한다.

			szErrorMessage = "FHS hash extract failed.";

			if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
			{
				_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s][%s]: [%s]"
					, getpid(), GetThreadId(), szClientAddress
					, szService.c_str(), szUri.c_str(), szErrorMessage.c_str() );
			}
			else
			{
				_LOG( LINF, "[SizeHash %d %ld] client[%s] error response send ok. [%s][%s]: [%s]"
					, getpid(), GetThreadId(), szClientAddress
					, szService.c_str(), szUri.c_str(), szErrorMessage.c_str() );
			}
		}

		// 다음 request 대기

	} // while(1) 


	// client 세션 종료 처리.
	clientSocket.Close();
	_LOG( LINF, "[SizeHash %d %ld] client[%s] connection close.", getpid(), GetThreadId(), szClientAddress );

	// Thread 종료 관련 처리.
	return NULL;
}


// Worker::SizeHash 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerSizeHashMain()
{
	// 전역 변수로 설정된 socket 중 해당 worker 에서 사용하지 않는 socket 은 close 처리한다.

	// Process Rename..
	set_ps_display( PROG_NAME": SizeHash", false );

	// Set Signal Handler
	SetWorkerSizeHashSignalHandler();

	// 1. 사용할 객체 초기화 처리


	// 2. 각 모듈 기동 처리
	// - 최종 작업 모듈부터 기동 처리

	if( g_objServiceInfo.Start() == false )
	{
		_LOG( LWAR, "[SizeHash %d] service info thread start failed. => Process Exit", getpid() );

		ReadyToExitWorkerSizeHash();
		return EXIT_FAILURE;	// EXIT_FAILURE 를 반환하여 재생성 처리 방지...
	}

	// 3. 수신 처리 관련 변수 정의 및 초기화.
	int clientSock;
	struct sockaddr_in clientSockAddr;
	socklen_t clientSockLen = sizeof( clientSockAddr );

	pthread_t clientThreadId;
	struct stSizeHashThreadArg * pThreadArg = NULL;

	// log
	_LOG( LINF, "[SizeHash %d] start waiting for client connection." , getpid());

	while( 1 )
	{
		clientSock = accept( g_listenSocketSizeHash, (struct sockaddr *)&clientSockAddr, &clientSockLen );

		if( clientSock == -1 )
		{
			// 오류 발생시.... 해당 내역 로깅 처리.
			int errorNum = errno;
			_LOG( LERR, "[SizeHash %d] client accept failed [%d][%s]", getpid(), errorNum, strerror( errorNum ) );
		}
		else
		{
			// 정상적인 Client 인 경우...

			// Thread 로 전달할 변수를 malloc 로 생성 처리
			pThreadArg = (struct stSizeHashThreadArg *)malloc( sizeof( struct stSizeHashThreadArg ) );
			if( pThreadArg == NULL )
			{
				int errorNum = errno;
				close( clientSock );    // client 접속 종료 처리.

				char tempBuffer[IP_ADDR_LENGTH];
				memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
				if( inet_ntop( AF_INET, &( clientSockAddr.sin_addr ), tempBuffer, sizeof( tempBuffer ) ) != NULL )
				{
					_LOG( LERR, "[SizeHash %d] client[%s] thread malloc failed [%d][%s]", getpid(), tempBuffer, errorNum, strerror( errorNum ) );
				}
				else
				{
					_LOG( LERR, "[SizeHash %d] client thread malloc failed [%d][%s] and inet_ntop error[%d][%s]"
						, getpid(), errorNum, strerror( errorNum ), errno, strerror( errno ) );
				}

				continue;
			}
			else
			{
				// 인자로 전달할 변수정보 설정.
				pThreadArg->clientSocket = clientSock;

				char tempBuffer[IP_ADDR_LENGTH];
				memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
				if( inet_ntop( AF_INET, &( clientSockAddr.sin_addr ), tempBuffer, sizeof( tempBuffer ) ) != NULL )
				{
					memcpy( pThreadArg->szClientAddress, tempBuffer, sizeof( tempBuffer ) );
				}
				else
				{
					// inet_ntop 오류 발생시
					// ip 가 불분명할 경우.. 관련 상세 내역 로깅 후.. 세션 종료 처리한다.
					//snprintf( pThreadArg->szClientAddress, IP_ADDR_LENGTH - 1, "0.0.0.0" );

					int errorNum = errno;
					_LOG( LERR, "[SizeHash %d] client ip not valid. connection closed. inet_ntop error[%d][%s]", getpid(), errorNum, strerror( errorNum ) );

					close( clientSock );
					free( pThreadArg );

					continue;
				}

				// Client 처리를 담당할 Thread 생성 처리
				if( pthread_create( &clientThreadId, NULL, WorkerSizeHashClientThread, (void *)pThreadArg ) != 0 )
				{
					// 생성 실패시...
					// - 로깅 
					// - client socket close 
					// - malloc 로 생성된 전달 변수 free 처리.

					int errorNum = errno;
					_LOG( LERR, "[SizeHash %d] client[%s] thread create failed [%d][%s]"
						, getpid(), pThreadArg->szClientAddress, errorNum, strerror( errorNum ) );

					close( clientSock );
					free( pThreadArg );
				}
				else
				{
					_LOG( LDBG, "[SizeHash %d] client[%s] accept & client thread create ok."
						, getpid(), pThreadArg->szClientAddress );
				}
			}
		}
	} // while(1)


	// 종료시 정리 작업 수행
	ReadyToExitWorkerSizeHash();
	return EXIT_SUCCESS;
}

