/*
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/limits.h>
#include <stdlib.h>
#include <libsafec/safe_str_lib.h>
#include <libsafec/safe_mem_lib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>

#include <amqp.h>
#include <amqp_tcp_socket.h>
#include "Common.h"
#include "StringUtil.h"
#include "Config.h"
#include "Log.h"
#include "DataStructure.h"
*/
#include "Socket.h"

const uint16_t CRC16Table[256] =
{
    0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
    0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
    0x0919, 0x1890, 0x2A0B, 0x3B82, 0x4F3D, 0x5EB4, 0x6C2F, 0x7DA6,
    0x8551, 0x94D8, 0xA643, 0xB7CA, 0xC375, 0xD2FC, 0xE067, 0xF1EE,
    0x1232, 0x03BB, 0x3120, 0x20A9, 0x5416, 0x459F, 0x7704, 0x668D,
    0x9E7A, 0x8FF3, 0xBD68, 0xACE1, 0xD85E, 0xC9D7, 0xFB4C, 0xEAC5,
    0x1B2B, 0x0AA2, 0x3839, 0x29B0, 0x5D0F, 0x4C86, 0x7E1D, 0x6F94,
    0x9763, 0x86EA, 0xB471, 0xA5F8, 0xD147, 0xC0CE, 0xF255, 0xE3DC,
    0x2464, 0x35ED, 0x0776, 0x16FF, 0x6240, 0x73C9, 0x4152, 0x50DB,
    0xA82C, 0xB9A5, 0x8B3E, 0x9AB7, 0xEE08, 0xFF81, 0xCD1A, 0xDC93,
    0x2D7D, 0x3CF4, 0x0E6F, 0x1FE6, 0x6B59, 0x7AD0, 0x484B, 0x59C2,
    0xA135, 0xB0BC, 0x8227, 0x93AE, 0xE711, 0xF698, 0xC403, 0xD58A,
    0x3656, 0x27DF, 0x1544, 0x04CD, 0x7072, 0x61FB, 0x5360, 0x42E9,
    0xBA1E, 0xAB97, 0x990C, 0x8885, 0xFC3A, 0xEDB3, 0xDF28, 0xCEA1,
    0x3F4F, 0x2EC6, 0x1C5D, 0x0DD4, 0x796B, 0x68E2, 0x5A79, 0x4BF0,
    0xB307, 0xA28E, 0x9015, 0x819C, 0xF523, 0xE4AA, 0xD631, 0xC7B8,
    0x48C8, 0x5941, 0x6BDA, 0x7A53, 0x0EEC, 0x1F65, 0x2DFE, 0x3C77,
    0xC480, 0xD509, 0xE792, 0xF61B, 0x82A4, 0x932D, 0xA1B6, 0xB03F,
    0x41D1, 0x5058, 0x62C3, 0x734A, 0x07F5, 0x167C, 0x24E7, 0x356E,
    0xCD99, 0xDC10, 0xEE8B, 0xFF02, 0x8BBD, 0x9A34, 0xA8AF, 0xB926,
    0x5AFA, 0x4B73, 0x79E8, 0x6861, 0x1CDE, 0x0D57, 0x3FCC, 0x2E45,
    0xD6B2, 0xC73B, 0xF5A0, 0xE429, 0x9096, 0x811F, 0xB384, 0xA20D,
    0x53E3, 0x426A, 0x70F1, 0x6178, 0x15C7, 0x044E, 0x36D5, 0x275C,
    0xDFAB, 0xCE22, 0xFCB9, 0xED30, 0x998F, 0x8806, 0xBA9D, 0xAB14,
    0x6CAC, 0x7D25, 0x4FBE, 0x5E37, 0x2A88, 0x3B01, 0x099A, 0x1813,
    0xE0E4, 0xF16D, 0xC3F6, 0xD27F, 0xA6C0, 0xB749, 0x85D2, 0x945B,
    0x65B5, 0x743C, 0x46A7, 0x572E, 0x2391, 0x3218, 0x0083, 0x110A,
    0xE9FD, 0xF874, 0xCAEF, 0xDB66, 0xAFD9, 0xBE50, 0x8CCB, 0x9D42,
    0x7E9E, 0x6F17, 0x5D8C, 0x4C05, 0x38BA, 0x2933, 0x1BA8, 0x0A21,
    0xF2D6, 0xE35F, 0xD1C4, 0xC04D, 0xB4F2, 0xA57B, 0x97E0, 0x8669,
    0x7787, 0x660E, 0x5495, 0x451C, 0x31A3, 0x202A, 0x12B1, 0x0338,
    0xFBCF, 0xEA46, 0xD8DD, 0xC954, 0xBDEB, 0xAC62, 0x9EF9, 0x8F70
};

const uint32_t CRC32Table[] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};


//int gnListeningSocket;

int g_eConnectionType;
char g_szConnectionIP[MAX_STRING_SIZE + 1];
int  g_nServicePort;
int g_nMaxConnections;
bool g_bUseTCPNoDelay;
int g_nChannelCount = 0;
int g_nChannelTypeFlags[SOCK_CONF_MAX_CHANNEL_LIMIT];
int g_nListeningSocket = -1;
bool g_bSocketProcessLoop;
int g_nErrorTimeout = 5000;                 // 지정된 시간동안 응답이 없는 경우 외부에 타임아웃 에러 전송(단위 : 밀리세컨드)
int g_nTimeoutStoreTerm = 30000;            // 메모리 리스트에서 저장되고 있는 시간(단위 : 밀리세컨드) - 외부에는 이미 타임아웃 오류가 전송된 상태
                                            // TIMEOUT_STORE_TERM >  ERROR_TIMEOUT
int g_nConnectionTimeout = 30000;           // 데이터 통신이 설정된 값동안 없으면 연결 종료(단위 : 초)
bool g_bUseGWTimeout = false;

int g_nMessageUniqueueKeyPos;
int g_nMessageUniqueueKeyLen;

pthread_mutex_t g_sThreadLoopMutex = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t g_sChannelMQThreadInfoMutex = PTHREAD_MUTEX_INITIALIZER;

struct SOCKET_THREAD_INFO **g_pChannelMQThreadInfo;

struct DOUBLELINKEDLISTINFO g_sConnectedSocketThreadInfo;

int (*g_fnProcessMessageSocketData)(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo) = NULL;
int (*g_fnProcessFileSocketData)(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo) = NULL;
int (*g_fnProcessMessageMQSendData)(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo, char *pData, int nDataLength, char *szCorrelationID, int nCorrelationIDLength, char *szResponseQueuename, int nReplyQueuenameLength, bool *bMQConnection, bool *bChannelOpen, uint64_t nDeliveryTag) = NULL;
int (*g_fnProcessMessageMQRespData)(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo, char *pData, int nDataLength, char *szCorrelationID, int nCorrelationIDLength, char *szResponseQueuename, int nReplyQueuenameLength, bool *bMQConnection, bool *bChannelOpen, uint64_t nDeliveryTag) = NULL;
int (*g_fnProcessFileMQData)(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo, char *pData, int nDataLength, char *szCorrelationID, int nCorrelationIDLength, char *szResponseQueuename, int nReplyQueuenameLength, bool *bMQConnection, bool *bChannelOpen, uint64_t nDeliveryTag) = NULL;
void (*g_fnInitializeFileTransferData)(int nSocketIndex) = NULL;
bool GetApplicationLoopState();

struct VALUE_STRING_PAIR_INFO g_sConnTypeInfo[] =
{
    { SOCK_CONN_TYPE_SERVER,        "Server"        },
    { SOCK_CONN_TYPE_CLIENT,        "Client"        },
};


struct VALUE_STRING_PAIR_INFO g_sChannelTypeInfo[] =
{
    { SOCK_CHANNEL_MESSAGE_SEND,    "MESSAGE_SEND" },
    { SOCK_CHANNEL_MESSAGE_RECV,    "MESSAGE_RECV" },
    { SOCK_CHANNEL_FILE_SEND,       "FILE_SEND"    },
    { SOCK_CHANNEL_FILE_RECV,       "FILE_RECV"    },
};

struct VALUE_STRING_PAIR_INFO g_sRespCodeInfo[] =
{
    { RESP_NORMAL,                  "0000",        },
    { RESP_LENGTH_ERROR,            "0001",        },
    { RESP_TIMEOUT,                 "0002",        },
    { RESP_SYSTEM_FAIL,             "0003",        },
    { RESP_ORG_ID_FAIL,             "0004",        },
    { RESP_FORMAT_ERROR,            "0005",        },
    { RESP_ALREADY_RECV,            "0006",        },
    { RESP_UNKNOWN_WORKTYPE,        "0007",        },
    { RESP_ABNORMAL_FILENAME,       "0008",        },
    { RESP_LENGTH_ABNORMAL,         "0009",        },
    { RESP_PROTOCOL_ERROR,          "0010",        },
    { RESP_FILE_TRANSFERING,        "0011",        },
    { RESP_ETC,                     "0999",        },
};


int InitSocketConfig()
{
    char szKeyTemp[MAX_STRING_SIZE + 1];
    char szValueTemp[MAX_STRING_SIZE + 1];
    int nChannelNo;

    // Read CONNECTION_TYPE
    if (GetConfigValue(SOCK_CONF_CONN_TYPE_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket CONNECTION_TYPE configuration not found[Available Value:Server or Client]!");
        return SOCK_ERR_CONFIG;
    }
    if (MappingStringToInt(szValueTemp, sizeof(szValueTemp), g_sConnTypeInfo, sizeof(g_sConnTypeInfo)/sizeof(struct VALUE_STRING_PAIR_INFO), &g_eConnectionType) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket Connection Type configuration error[ConfigFile:%s,Available Value:Server or Client]!", szValueTemp);
        return SOCK_ERR_CONFIG;
    }

    // Read TARGET_IP
    if (GetConfigValue(SOCK_CONF_TARGET_IP_KEY, g_szConnectionIP, sizeof(g_szConnectionIP)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket TARGET_IP configuration not found!");
        return SOCK_ERR_CONFIG;
    }
    
    // Read TARGET_PORT
    if (GetConfigValue(SOCK_CONF_TARGET_PORT_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket TARGET_PORT configuration not found!");
        return SOCK_ERR_CONFIG;
    }
    if (SafeStringToInt(szValueTemp, sizeof(szValueTemp), &g_nServicePort) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket TARGET_PORT configuration error[ConfigFile:%s]!", szValueTemp);
        return SOCK_ERR_CONFIG;
    }

    // Read TCP_NODELAY
    if (GetConfigValue(SOCK_CONF_TCP_NODELAY_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        g_bUseTCPNoDelay = false;
    }
    else
    {
        if (ParsingYESNO(szValueTemp, sizeof(szValueTemp), &g_bUseTCPNoDelay) != CONF_ERR_SUCCESS)
        {
            g_bUseTCPNoDelay = false;
        }
    }

    // Read MAX_CONNECTION
    if (GetConfigValue(SOCK_CONF_MAX_CONNECTION_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket MAX_CONNECTION configuration notfound(set default value: 1)");
        g_nMaxConnections = 1;
    }
    else
    {
        if (SafeStringToInt(szValueTemp, sizeof(szValueTemp), &g_nMaxConnections) != CONF_ERR_SUCCESS)
        {
            WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket MAX_CONNECTION configuration notfound(set default value: 1)!");
            g_nMaxConnections = 1;
        }
    }


    // Read ERROR_TIMEOUT
    if (GetConfigValue(SOCK_CONF_ERROR_TIMEOUT_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket ERROR_TIMEOUT configuration notfound(set default value: 5000)");
        g_nErrorTimeout = 5000;
    }
    else
    {
        if (SafeStringToInt(szValueTemp, sizeof(szValueTemp), &g_nErrorTimeout) != CONF_ERR_SUCCESS)
        {
            WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket ERROR_TIMEOUT configuration notfound(set default value: 5000)!");
            g_nErrorTimeout = 5000;
        }
    }

    // Read TIMEOUT_STORE_TERM
    if (GetConfigValue(SOCK_CONF_TIMEOUT_STORE_TERM_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket TIMEOUT_STORE_TERM configuration notfound(set default value: 30000)");
        g_nErrorTimeout = 30000;
    }
    else
    {
        if (SafeStringToInt(szValueTemp, sizeof(szValueTemp), &g_nTimeoutStoreTerm) != CONF_ERR_SUCCESS)
        {
            WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket TIMEOUT_STORE_TERM configuration notfound(set default value: 30000)!");
            g_nErrorTimeout = 30000;
        }
    }

    // Read CONNECTION_TIMEOUT
    if (GetConfigValue(SOCK_CONF_CONNECTION_TIMEOUT_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket CONNECTION_TIMEOUT configuration notfound(set default value: 300000)");
        g_nErrorTimeout = 300000;
    }
    else
    {
        if (SafeStringToInt(szValueTemp, sizeof(szValueTemp), &g_nConnectionTimeout) != CONF_ERR_SUCCESS)
        {
            WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket CONNECTION_TIMEOUT configuration notfound(set default value: 300000)!");
            g_nErrorTimeout = 300000;
        }
    }

    // Read USE_GW_TIMEOUT
    if (GetConfigValue(SOCK_CONF_USE_GW_TIMEOUT_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        g_bUseGWTimeout = false;
    }
    else
    {
        if (ParsingYESNO(szValueTemp, sizeof(szValueTemp), &g_bUseGWTimeout) != CONF_ERR_SUCCESS)
        {
            g_bUseGWTimeout = false;
        }
    }

    //SOCK_CONF_MAX_CHANNEL_LIMIT
    // Read CHANNEL_COUNT
    if (GetConfigValue(SOCK_CONF_CHANNEL_COUNT_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket CHANNEL_COUNT configuration not found!");
        return SOCK_ERR_CONFIG;
    }
    if (SafeStringToInt(szValueTemp, sizeof(szValueTemp), &g_nChannelCount) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket CHANNEL_COUNT configuration error[ConfigFile:%s]!", szValueTemp);
        return SOCK_ERR_CONFIG;
    }
    if (g_nChannelCount > SOCK_CONF_MAX_CHANNEL_LIMIT)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket CHANNEL_COUNT configuration value is too big[Max:%d,ConfigFile:%d]!", SOCK_CONF_MAX_CHANNEL_LIMIT, g_nChannelCount);
        return SOCK_ERR_CONFIG;
    }

    for (nChannelNo = 0; nChannelNo < g_nChannelCount; nChannelNo++)
    {
        snprintf(szKeyTemp, sizeof(szKeyTemp), "CHANNEL%d_TYPE", nChannelNo + 1);
        if (GetConfigValue(szKeyTemp, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
        {
            WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket %s configuration not found", szKeyTemp);
            return SOCK_ERR_CONFIG;
        }
        if (ParsingFlagString(szValueTemp, sizeof(szValueTemp), g_sChannelTypeInfo, sizeof(g_sChannelTypeInfo)/ sizeof(struct VALUE_STRING_PAIR_INFO), &g_nChannelTypeFlags[nChannelNo]) != CONF_ERR_SUCCESS)
        {
            WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket %s configuration error[ConfigFile:%s]!", szKeyTemp, szValueTemp);
            return SOCK_ERR_CONFIG;
        }
    }
    // Read MESSAGE_UNIQUEUE_KEY_POSITION_KEY
    if (GetConfigValue(MESSAGE_UNIQUEUE_KEY_POSITION_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket MESSAGE_UNIQUEUE_KEY_POSITION configuration not found!");
        return SOCK_ERR_CONFIG;
    }
    if (SafeStringToInt(szValueTemp, sizeof(szValueTemp), &g_nMessageUniqueueKeyPos) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket MESSAGE_UNIQUEUE_KEY_POSITION configuration error[ConfigFile:%s]!", szValueTemp);
        return SOCK_ERR_CONFIG;
    }
    // Read MESSAGE_UNIQUEUE_KEY_LENGTH_KEY
    if (GetConfigValue(MESSAGE_UNIQUEUE_KEY_LENGTH_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket MESSAGE_UNIQUEUE_KEY_LENGTH configuration not found!");
        return SOCK_ERR_CONFIG;
    }
    if (SafeStringToInt(szValueTemp, sizeof(szValueTemp), &g_nMessageUniqueueKeyLen) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket MESSAGE_UNIQUEUE_KEY_LENGTH configuration error[ConfigFile:%s]!", szValueTemp);
        return SOCK_ERR_CONFIG;
    }


    return SOCK_ERR_SUCCESS;
}

int ListeningPort(const char *szIPAddress, const int nPort)
{
    struct sockaddr_in sAddrIn;
    int nSocketOptionValue;
    
    if (g_nListeningSocket == -1)
    {
        g_nListeningSocket = socket(PF_INET, SOCK_STREAM, 0);
        if (g_nListeningSocket == -1)
        {
#ifdef _WIN32
            TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
            FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                           NULL,
                           WSAGetLastError(),
                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                           szErrorMsg,
                           sizeof(szErrorMsg),
                           NULL);
            WriteLog(LogType_ErrorOut, LogLevel_Error, "Listening Socket creation fail[%s]", szErrorMsg);
#else
            WriteLog(LogType_ErrorOut, LogLevel_Error, "Listening Socket creation fail[%s]", strerror(errno));
#endif
            return  SOCK_ERR_INVALID_SOCKET;
        }

        nSocketOptionValue = 1;
#ifdef _WIN32
        if (setsockopt(g_nListeningSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&nSocketOptionValue, sizeof(int)) == -1)
#else
        if (setsockopt(g_nListeningSocket, SOL_SOCKET, SO_REUSEADDR, (int *)&nSocketOptionValue, sizeof(int)) == -1)
#endif
        {
#ifdef _WIN32
            TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
            FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                           NULL,
                           WSAGetLastError(),
                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                           szErrorMsg,
                           sizeof(szErrorMsg),
                           NULL);
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "socket setsockopt(SO_REUSEADDR) fail[%s]", szErrorMsg);
#else
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "socket setsockopt(SO_REUSEADDR) fail[%s]", strerror(errno));
#endif
            return  SOCK_ERR_SETSOCKOPT;
        }
        SetSocketBlockingEnabled(g_nListeningSocket, false);
    }

    SafeMemSet(&sAddrIn, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
    sAddrIn.sin_family = AF_INET;
    sAddrIn.sin_port = htons(g_nServicePort);
    //sAddrIn.sin_addr.s_addr = htonl(INADDR_ANY);

    if (inet_pton(AF_INET, g_szConnectionIP, &sAddrIn.sin_addr) != 1)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "IP Convert Error![%s]", g_szConnectionIP);
        return SOCK_ERR_CONFIG;
    }

    if (bind(g_nListeningSocket, (struct sockaddr *)&sAddrIn, sizeof(sAddrIn)) == -1)
    {
#ifdef _WIN32
        TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL,
                       WSAGetLastError(),
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       szErrorMsg,
                       sizeof(szErrorMsg),
                       NULL);
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket bind Error![IP:%s, Port:%d, MSG:%s]", szIPAddress, nPort, szErrorMsg);
#else
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket bind Error![IP:%s, Port:%d, MSG:%s]", szIPAddress, nPort, strerror(errno));

#endif
        return SOCK_ERR_BIND;
    }

    if (listen(g_nListeningSocket, 20) == -1)
    {
#ifdef _WIN32
        TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL,
                       WSAGetLastError(),
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       szErrorMsg,
                       sizeof(szErrorMsg),
                       NULL);
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket listen Error![MSG:%s]", szErrorMsg);
#else
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket listen Error![MSG:%s]", strerror(errno));

#endif
        return SOCK_ERR_LISTEN;
    }

    return SOCK_ERR_SUCCESS;
}

void RemoveTerminatedThread()
{
    struct SOCKET_THREAD_INFO *pThreadInfo;
    struct DOUBLELINKEDLISTNODE *pListNode;
    int nResult;
    int *pnReturnValue;

    nResult = DoubleLinkedListMoveToHead(&g_sConnectedSocketThreadInfo, &pListNode);

    if (pListNode == NULL)
        return;

    //printf_s("%d Thread Node Exists!\n", g_sConnectedSocketThreadInfo.nNodeCount);

    while (nResult == DS_ERR_SUCCESS && pListNode != NULL)
    {
        pThreadInfo = (struct SOCKET_THREAD_INFO *)pListNode->pNodeData;
        pthread_mutex_lock(&pThreadInfo->sContextMutex);
        if (pThreadInfo->nRunState == SOCK_THREAD_STATE_TERMINATED)
        {
            pthread_mutex_unlock(&pThreadInfo->sContextMutex);
/*
            pthread_mutex_lock(&g_sChannelMQThreadInfoMutex);
            g_pChannelMQThreadInfo[pThreadInfo->nSocketIndex] = (struct SOCKET_THREAD_INFO *)NULL;
            pthread_mutex_unlock(&g_sChannelMQThreadInfoMutex);
*/
#ifdef      __MINGW64__
            nResult = _pthread_tryjoin(pThreadInfo->nThreadID, &pnReturnValue);
#else
            nResult = pthread_tryjoin_np(pThreadInfo->nThreadID, &pnReturnValue);
#endif
            if (nResult == 0) // 상태 변수는 프로그램에서 콘트롤 하므로 실제로 종료 되었는지도 체크 한다.
            {
                nResult = DoubleLinkedListRemoveCurrentNode(&g_sConnectedSocketThreadInfo, &pListNode);
                DestroyCHANNEL_BUFFER(pThreadInfo);
                SafeFree((void **)&pThreadInfo);
                SafeFree((void **)&pListNode);
                SafeFree((void **)&pnReturnValue);
                nResult = DoubleLinkedListGetCurrentNode(&g_sConnectedSocketThreadInfo, &pListNode);
            }
            else
            {
                pthread_mutex_unlock(&pThreadInfo->sContextMutex);
                nResult = DoubleLinkedListMoveToNext(&g_sConnectedSocketThreadInfo, &pListNode);
            }
        }
        else
        {
            pthread_mutex_unlock(&pThreadInfo->sContextMutex);
            nResult = DoubleLinkedListMoveToNext(&g_sConnectedSocketThreadInfo, &pListNode);
        }
    }
}

int ListeningLoop()
{
    int nSocketOptionValue;
    struct sockaddr_in sAddrIn;
    struct linger sLinger;
    int nAcceptedSocket;
    struct SOCKET_THREAD_INFO *pThreadInfo;
    struct DOUBLELINKEDLISTNODE *pListNode;
    int nSocketIndex;
    socklen_t sAddrLen;
    fd_set sReadFDSet;
    int nResult;
    int nRemotePort;
    struct timeval sTimeout;

    char szIpAddrTemp[MAX_STRING_SIZE + 1];
    pThreadInfo = NULL;
    pListNode = NULL;


    //while (g_bSocketProcessLoop)
    while (GetApplicationLoopState())
    {
        RemoveTerminatedThread();

        sAddrLen = sizeof(sAddrIn);

        // TODO : cpu 사용율을 제어하기 위한 select 기능 추가 진행
        FD_ZERO(&sReadFDSet);
        FD_SET(g_nListeningSocket, &sReadFDSet);

        sTimeout.tv_sec = 0;
        sTimeout.tv_usec = 100;

        nResult = select(g_nListeningSocket + 1, &sReadFDSet, NULL, NULL, &sTimeout);
        if (nResult > 0 && FD_ISSET(g_nListeningSocket, &sReadFDSet))
        {
            if ((nAcceptedSocket = accept(g_nListeningSocket, (struct sockaddr *)&sAddrIn, &sAddrLen)) == -1)
            {
    #ifdef _WIN32
                TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
                WSADATA wsaData;
                FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                               NULL,
                               WSAGetLastError(),
                               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                               szErrorMsg,
                               sizeof(szErrorMsg),
                               NULL);

                switch(WSAGetLastError())
                {
                case WSAEWOULDBLOCK:
                case WSAEINPROGRESS: // A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                case WSAECONNRESET:
                case WSAEOPNOTSUPP:
                    close(g_nListeningSocket);
                    g_nListeningSocket = -1;
                case WSANOTINITIALISED: // 정상적인 초기화 필요
                case WSAENETDOWN:       // network subsystem down
                    g_nListeningSocket = -1;
                    if (WSAStartup(MAKEWORD(1,1), &wsaData) == SOCKET_ERROR)
                    {
                        WriteLog(LogType_StandardOut, LogLevel_Info,"Error initialising WSA.");
                        continue;
                    }
                    ListeningPort(g_szConnectionIP, g_nServicePort);
                    continue;
                case WSAENOTSOCK:
                case WSAEINVAL:
                    g_nListeningSocket = -1;
                    ListeningPort(g_szConnectionIP, g_nServicePort);
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                case WSAEINTR: // 확인 필요 WSACancelBlockingCall이 호출된 경우 발생
                    // EMFILE은 File Descriptor가 MAX인 상태라 해소 될수도 있고 해소되지 않을 수도 있다. 무시할지 다른 처리가 필요할지 고민 필요
                    WriteLog(LogType_StandardOut, LogLevel_Info, "Socket accept Error![Error Msg : %s]", szErrorMsg);
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                case WSAEMFILE:
                    WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Too many file opened in %s", g_szProcessname);
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                    // ENFILE은 File Descriptor가 MAX인 상태라 해소 될수도 있고 해소되지 않을 수도 있다. 무시할지 다른 처리가 필요할지 고민 필요
                case ENFILE:
                    WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Too many file opened in system");
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                case WSAENOBUFS:
                case ENOMEM:
                case WSAEFAULT:
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                }
    #else
                switch(errno)
                {
                //case EWOULDBLOCK:
                case EAGAIN:
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                case ECONNABORTED:
                    close(g_nListeningSocket);
                case EBADF:
                case ENOTSOCK:
                    g_nListeningSocket = -1;
                    ListeningPort(g_szConnectionIP, g_nServicePort);
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                case EINTR:
                    // EMFILE은 File Descriptor가 MAX인 상태라 해소 될수도 있고 해소되지 않을 수도 있다. 무시할지 다른 처리가 필요할지 고민 필요
                    WriteLog(LogType_StandardOut, LogLevel_Info, "Socket accept Error![Error Msg : %s]", strerror(errno));
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                case EMFILE:
                    WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Too many file opened in %s", g_szProcessname);
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                    // ENFILE은 File Descriptor가 MAX인 상태라 해소 될수도 있고 해소되지 않을 수도 있다. 무시할지 다른 처리가 필요할지 고민 필요
                case ENFILE:
                    WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Too many file opened in system");
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                case ENOBUFS:
                case ENOMEM:
                case EPROTO:
                    //usleep(SOCK_PROCESS_SLEEP);
                    continue;
                }
    #endif
            }
        }
        else
        {
            continue;
        }
        nRemotePort = ntohs(sAddrIn.sin_port);
        if (inet_ntop(AF_INET, &sAddrIn.sin_addr, szIpAddrTemp, sizeof(szIpAddrTemp)) == (char *)NULL)
        {
            WriteLog(LogType_StandardOut, LogLevel_Info, "Can't get client IP Address!");
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Can't get client IP Address![%s]", strerror(errno));
        }
        else
        {
            WriteLog(LogType_StandardOut, LogLevel_Info, "Connection Established![IP : %s, Port : %d]", szIpAddrTemp, nRemotePort);
        }
        sLinger.l_onoff = 1;
        sLinger.l_linger = 0;

#ifdef _WIN32
        if (setsockopt(nAcceptedSocket, SOL_SOCKET, SO_LINGER, (char *)&sLinger, sizeof(sLinger)) == -1)
#else
        if (setsockopt(nAcceptedSocket, SOL_SOCKET, SO_LINGER, &sLinger, sizeof(sLinger)) == -1)
#endif
        {
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "socket setsockopt(SO_LINGER) fail[%s]", strerror(errno));
            //return  SOCK_ERR_SETSOCKOPT;
        }
        if (g_sConnectedSocketThreadInfo.nNodeCount >= g_nMaxConnections)
        {
//            sLinger.l_onoff = 1;
//            sLinger.l_linger = 0;
//
//            if (setsockopt(nAcceptedSocket, SOL_SOCKET, SO_LINGER, &sLinger, sizeof(sLinger)) == -1)
//            {
//                WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "socket setsockopt(SO_LINGER) fail[%s]", strerror(errno));
//                //return  SOCK_ERR_SETSOCKOPT;
//            }
            close(nAcceptedSocket);
            continue;
        }
        nSocketOptionValue = 1;
#ifdef _WIN32
        if (setsockopt(g_nListeningSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&nSocketOptionValue, sizeof(int)) == -1)
#else
        if (setsockopt(g_nListeningSocket, SOL_SOCKET, SO_REUSEADDR, (int *)&nSocketOptionValue, sizeof(int)) == -1)
#endif
        {
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "socket setsockopt(SO_REUSEADDR) fail[%s]", strerror(errno));
        }
        pThreadInfo = NULL;
        nResult = SafeMemoryAlloc((void **)&pThreadInfo, sizeof(struct SOCKET_THREAD_INFO));
        //pThreadInfo = SafeAlloc(pThreadInfo, sizeof(struct SOCKET_THREAD_INFO));
        SafeMemSet(pThreadInfo, sizeof(struct SOCKET_THREAD_INFO), 0, sizeof(struct SOCKET_THREAD_INFO));
        if (ServerMQChannelAllocation(pThreadInfo, &nSocketIndex) != SOCK_ERR_SUCCESS)
        {
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Message Queue Channel Allocation Error!");
        }
        pthread_mutex_init(&pThreadInfo->sContextMutex, NULL);
        pthread_mutex_init(&pThreadInfo->sPublishMQ.sMQMutex, NULL);
        pthread_mutex_init(&pThreadInfo->sConsumeMQ.sMQMutex, NULL);
        pthread_mutex_lock(&pThreadInfo->sContextMutex);
        pThreadInfo->nSocket = nAcceptedSocket;
        SafeStrNCopy(pThreadInfo->szRemoteIP, sizeof(pThreadInfo->szRemoteIP), szIpAddrTemp, strnlen(szIpAddrTemp, sizeof(szIpAddrTemp)));
        pThreadInfo->nRemotePort = nRemotePort;
        pThreadInfo->nSocketIndex = nSocketIndex;
        pThreadInfo->bThreadLoop = true;
        pThreadInfo->nRunState = SOCK_THREAD_STATE_INIT;
        pthread_mutex_unlock(&pThreadInfo->sContextMutex);
        InitCHANNEL_BUFFER(pThreadInfo);
        DoubleLinkedListAddNodeToHead(&g_sConnectedSocketThreadInfo, pThreadInfo, &pListNode);

        /*
         * 각 소켓에 대해 메시지 큐 응답을 받을려면 connection 마다 큐가 하나 더 필요하다.
         * 설계는 머리에서 완료 되었으나 프로그램 내부에서 각 connection에 대한 인덱스를 어떻게 할 것인지?
         * 1안 linkedlist를 사용하면서 index계산을 위한 내부 메모리를 별도로 가져갈지?
         * 2안 배열을 MaxConnection 크기만큼 가지고 있으면서 관리할지?
         * Listening 상태에서는 연결을 받아들이는 입장이라 최대 socket이 사용되지 않을 수 있으므로 MessageQueue 채널 관리가 필요하다.
         */
        pthread_create(&pThreadInfo->nThreadID, NULL, &SocketProcessLoop, pThreadInfo);
        //usleep(SOCK_PROCESS_SLEEP);
    }
    return SOCK_ERR_SUCCESS;
}

int ConnectTo(const char *szHostname, const int nPort, int *pnSocket)
{
    int nSocketOptionValue;
    struct sockaddr_in sAddrIn;
    
    if (*pnSocket != -1)
    {
        close(*pnSocket);
        *pnSocket = -1;
    }
    *pnSocket = socket(PF_INET, SOCK_STREAM, 0);
    
    if (*pnSocket == -1)
    {
#ifdef _WIN32
        TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL,
                       WSAGetLastError(),
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       szErrorMsg,
                       sizeof(szErrorMsg),
                       NULL);
        WriteLog(LogType_ErrorOut, LogLevel_Error, "소켓생성 실패[%s]", szErrorMsg);
#else
        WriteLog(LogType_ErrorOut, LogLevel_Error, "소켓생성 실패[%s]", strerror(errno));
#endif
        return  SOCK_ERR_INVALID_SOCKET;
    }
    
    nSocketOptionValue = 1;
    
    if (setsockopt(*pnSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&nSocketOptionValue, sizeof(int)) == -1)
    {
#ifdef _WIN32
        TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL,
                       WSAGetLastError(),
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       szErrorMsg,
                       sizeof(szErrorMsg),
                       NULL);
        WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "socket setsockopt(TCP_NODELAY) fail[%s]", szErrorMsg);
#else
        WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "socket setsockopt(TCP_NODELAY) fail[%s]", strerror(errno));
#endif
        close(*pnSocket);
        *pnSocket = -1;
        return  SOCK_ERR_SETSOCKOPT;
    }
    
    
    SafeMemSet(&sAddrIn, sizeof(sAddrIn), 0, sizeof(sAddrIn));
    sAddrIn.sin_family = AF_INET;
    sAddrIn.sin_port = htons(nPort);
    
    if (inet_pton(AF_INET, szHostname, &sAddrIn.sin_addr) != 1)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "IP Convert Error![%s]", g_szConnectionIP);
        return SOCK_ERR_CONFIG;
    }
    
    if (connect(*pnSocket, (struct sockaddr *)&sAddrIn, sizeof(sAddrIn)) != 0)
    {
#ifdef _WIN32
        char szErrorMsg[MAX_STRING_SIZE + 1];
        char szUTF8Msg[MAX_STRING_SIZE + 1];
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL,
                       WSAGetLastError(),
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       szErrorMsg,
                       sizeof(szErrorMsg),
                       NULL);
        ASCIIToUTF8(szErrorMsg, sizeof(szErrorMsg), szUTF8Msg, sizeof(szUTF8Msg));
        WriteLog(LogType_ErrorOut, LogLevel_Error, "소켓연결 실패[%s]", szUTF8Msg);
#else
        WriteLog(LogType_ErrorOut, LogLevel_Error, "소켓연결 실패[%s]", strerror(errno));
#endif
        close(*pnSocket);
        *pnSocket = -1;
        return SOCK_ERR_CONNECT;
    }

    if (SetSocketBlockingEnabled(*pnSocket, false) == false)
    {
        close(*pnSocket);
        *pnSocket = -1;
        return SOCK_ERR_NONBLOCK;
    }
    return SOCK_ERR_SUCCESS;

}

int InitSocket()
{
    //struct SOCKET_THREAD_INFO sClientThreadInfo;
    struct SOCKET_THREAD_INFO *pThreadInfo;
    struct DOUBLELINKEDLISTNODE *pListNode;
    struct timespec sJoinInterval;
    int i;
    int nResult;
    //int **ppThreadReturnValue;

    pThreadInfo = NULL;
    pListNode = NULL;

#ifdef _WIN32
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(1,1), &wsaData) == SOCKET_ERROR)
    {
        WriteLog(LogType_StandardOut, LogLevel_Info,"Error initialising WSA.");
        return SOCK_ERR_INVALID_SOCKET;
    }

#endif

    g_bSocketProcessLoop = true;
    nResult = SafeMemoryAlloc((void **)&g_pChannelMQThreadInfo, g_nMaxConnections * sizeof(struct SOCKET_THREAD_INFO *));
    //g_pChannelMQThreadInfo = SafeAlloc(g_pChannelMQThreadInfo, g_nMaxConnections * sizeof(struct SOCKET_THREAD_INFO *));
    SafeMemSet(g_pChannelMQThreadInfo, g_nMaxConnections * sizeof(struct SOCKET_THREAD_INFO *), 0, g_nMaxConnections * sizeof(struct SOCKET_THREAD_INFO *));
    switch (g_eConnectionType)
    {
    case SOCK_CONN_TYPE_SERVER:
        if (ListeningPort(g_szConnectionIP, g_nServicePort) != SOCK_ERR_SUCCESS)
        {
            WriteLog(LogType_StandardOut, LogLevel_Info,"socket bind fail[IP:%s, Port:%n]", g_szConnectionIP, g_nServicePort);
            return SOCK_ERR_BIND;
        }
        ListeningLoop();
        break;
    case SOCK_CONN_TYPE_CLIENT:
        /*
         *TODO : 최초 연결 실패시 프로그램 종료됨(수정필요)
         * SocketProcessLoop에 재접속 루틴이 있어 여기서 제거해도 되지 않을까?
         *          */
/*
        if (ConnectTo(g_szConnectionIP, g_nServicePort, &sClientThreadInf->nSocket) != SOCK_ERR_SUCCESS)
        {
            WriteLog(LogType_StandardOut, LogLevel_Info,"socket connect fail[IP:%s, Port:%n]", g_szConnectionIP, g_nServicePort);
            return SOCK_ERR_CONNECT;
        }
*/
        /*
         * TODO : Client connection 개수도 변경 가능하게 수정 필요(다중연결 지원 여부 확정 필요)
         *          1. Multi Process가 아닌 multi thread로 다중 connection 유지 필요(서버만 multi thread? 클라이언트는? 불필요한 process를 많이 만들 필요가 있나?)
         *          2. multi process와 thread의 주요 차이점은 메모리 오류등의 주요 오류가 있을대 multi process는 하나만 종료되고 thread는 모두 종료
         *          3. 어차피 버그가 있으면 늦던 빠르던 죽는다.(별도 프로세스로 인한 프로그램 종료 예방 효과는 매우 약하다.)
         *          4. 그렇다면 결론은 client도 multi thread가 더 좋다.
         *          5. 그럼 모니터링은 어떻게 할 것인가?
         *              1. Rabbitmq Queue 모니터링
         *              2. netstat -na에 나오는 내용중 연결설정 내용
         *              3. 연결상태를 DB나 로컬 파일에 저장하는 방법
         *              4. ELK Stack을 이용해 연결 및 종료 문자 확인하는 방법
         */
        for (i = 0; i < g_nMaxConnections; i++)
        {
            pThreadInfo = NULL;
            nResult = SafeMemoryAlloc((void **)&pThreadInfo, sizeof(struct SOCKET_THREAD_INFO));
            //pThreadInfo = SafeAlloc(pThreadInfo, sizeof(struct SOCKET_THREAD_INFO));
            SafeMemSet(pThreadInfo, sizeof(struct SOCKET_THREAD_INFO), 0, sizeof(struct SOCKET_THREAD_INFO));
            pthread_mutex_init(&pThreadInfo->sContextMutex, NULL);
            pThreadInfo->nSocket = -1;
            pThreadInfo->nRunState = SOCK_THREAD_STATE_INIT;
            //pthread_mutex_lock(&pThreadInfo->sContextMutex);
            pThreadInfo->nSocketIndex = i;
            pThreadInfo->bThreadLoop = true;
            //pthread_mutex_unlock(&pThreadInfo->sContextMutex);
            InitCHANNEL_BUFFER(pThreadInfo);
            DoubleLinkedListAddNodeToHead(&g_sConnectedSocketThreadInfo, pThreadInfo, &pListNode);
            /*
             * Client는 Thread 생성 후 Thread에서 연결 시도 하므로 이에 대한 채널은 고정으로 사용 가능하다.
             */
            pthread_create(&pThreadInfo->nThreadID, NULL, SocketProcessLoop, pThreadInfo);
        }
        //while (g_bSocketProcessLoop)
        sJoinInterval.tv_sec = 0;
        sJoinInterval.tv_nsec = 10000;
        while (GetApplicationLoopState())
        {
            nResult = DoubleLinkedListMoveToHead(&g_sConnectedSocketThreadInfo, &pListNode);
            while (nResult == DS_ERR_SUCCESS && pListNode != NULL)
            {
                pThreadInfo = (struct SOCKET_THREAD_INFO *)pListNode->pNodeData;

                // 다른 쓰레드 명령은 괜찮은데 pthread_timedjoin_np만 mingw에 없는것 같다. : 변경 필요
                //nResult = pthread_timedjoin_np(pThreadInfo->nThreadID, ppThreadReturnValue, &sJoinInterval);
                // Thread 종료됨 종료 상태 여부 확인해서 아니면 쓰레드 재시작
                // 상태 체크를 잘 해야 될것 같다. signal이 발생하였을 경우 다시 재시작 되면 안된다.
                if (nResult == 0)
                {

                }
                nResult = DoubleLinkedListMoveToNext(&g_sConnectedSocketThreadInfo, &pListNode);
            }
            usleep(SOCK_PROCESS_SLEEP);
        }
        break;
    }
    return SOCK_ERR_SUCCESS;
}

bool GetSocketThreadLoopState(struct SOCKET_THREAD_INFO *pThreadInfo)
{
    bool bThreadProcessLoop;

    pthread_mutex_lock(&pThreadInfo->sContextMutex);
    bThreadProcessLoop = pThreadInfo->bThreadLoop;
    pthread_mutex_unlock(&pThreadInfo->sContextMutex);
    return bThreadProcessLoop;
}

bool GetApplicationLoopState()
{
    bool bThreadProcessLoop;

    pthread_mutex_lock(&g_sThreadLoopMutex);
    bThreadProcessLoop = g_bSocketProcessLoop;
    pthread_mutex_unlock(&g_sThreadLoopMutex);
    return bThreadProcessLoop;
}


int RecvSocket(int nSocket, struct SOCKET_THREAD_INFO *pThreadInfo, bool *bConnected)
{
    int nRecvBytes;
    //int nBufferIndex;
    int nSTXIndex;
    int nETXIndex;
    int nChannelNo;
    int nSearchIndex;
    int nResult;
    char szCRC[SOCK_CRC_LENGTH + 1];
    char szUUIDString[UUID_STRING_SIZE + 1];
    uint16_t nCRCCalculated;
    uint16_t nCRCReceived;
    struct DOUBLELINKEDLISTNODE *pListNode;
    struct MESSAGE_INFO *pMessageInfo;

    pListNode = NULL;
    pMessageInfo = NULL;

    pthread_mutex_lock(&(pThreadInfo->sRecvBufferMutex));
    nRecvBytes = recv(nSocket, pThreadInfo->aRecvPacketBuffer + pThreadInfo->nRecvPacketBufferOffset, sizeof(pThreadInfo->aRecvPacketBuffer) - pThreadInfo->nRecvPacketBufferOffset - 1, 0);
    if (nRecvBytes == -1)
    {
#ifdef _WIN32
        TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
        WSADATA wsaData;
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL,
                       WSAGetLastError(),
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       szErrorMsg,
                       sizeof(szErrorMsg),
                       NULL);
        switch (WSAGetLastError())
        {
        case WSANOTINITIALISED:
            if (WSAStartup(MAKEWORD(1,1), &wsaData) == SOCKET_ERROR)
            {
                WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Error initialising WSA.[%s]", szErrorMsg);
                return SOCK_ERR_WSA_NOTINITIALIZED;
            }
            return SOCK_ERR_WSA_NOTINITIALIZED;
        case EAGAIN:
        case EINTR:
            pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
            return SOCK_ERR_NONBLOCK;
        case EFAULT:
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Socket Read Error[%s]", szErrorMsg);
            pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
            return SOCK_ERR_MEM;
        case EINVAL:
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Socket Read Error[%s]", szErrorMsg);
            *bConnected = false;
            pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
            return SOCK_ERR_INVALID_SOCKET;
        case ENOTCONN:
            close(nSocket);
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Socket Read Error[%s]", szErrorMsg);
            *bConnected = false;
            pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
            return SOCK_ERR_CONNECT;
        case EBADF:
        case ENOTSOCK:
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Socket Read Error[%s]", szErrorMsg);
            *bConnected = false;
            pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
            return SOCK_ERR_CONNECT;
        }

#else
        switch (errno)
        {
        case EAGAIN:
        case EINTR:
            pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
            return SOCK_ERR_NONBLOCK;
        case EFAULT:
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Socket Read Error[%s]", strerror(errno));
            pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
            return SOCK_ERR_MEM;
        case EINVAL:
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Socket Read Error[%s]", strerror(errno));
            *bConnected = false;
            pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
            return SOCK_ERR_INVALID_SOCKET;
        case ENOTCONN:
            close(nSocket);
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Socket Read Error[%s]", strerror(errno));
            *bConnected = false;
            pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
            return SOCK_ERR_CONNECT;
        case EBADF:
        case ENOTSOCK:
            WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Socket Read Error[%s]", strerror(errno));
            *bConnected = false;
            pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
            return SOCK_ERR_CONNECT;
        }
#endif
        pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
        return SOCK_ERR_READWRITE;
    }
    if (nRecvBytes == 0)
    {
        *bConnected = false;
        pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
        WriteLog(LogType_StandardOut, LogLevel_Info, "Recv 0 Bytes[%s]", strerror(errno));
        return SOCK_ERR_SUCCESS;
    }
    //void WriteRawCommLog(enum LogType eLogType, enum LogLevel eLogLevel, int nSocket, char cSendRecv, const char *szFormat, ...);
    pThreadInfo->aRecvPacketBuffer[pThreadInfo->nRecvPacketBufferOffset + nRecvBytes] = '\0';
    WriteRawCommLog(LogType_CommOut, LogLevel_Info, nSocket, 'R', "%s", pThreadInfo->aRecvPacketBuffer + pThreadInfo->nRecvPacketBufferOffset);


    if (pThreadInfo->aRecvPacketBuffer[pThreadInfo->nRecvPacketBufferOffset] == SOCK_STX_CHAR && pThreadInfo->nRecvPacketBufferOffset != 0)
    {
        memmove_s(pThreadInfo->aRecvPacketBuffer, sizeof(pThreadInfo->aRecvPacketBuffer), pThreadInfo->aRecvPacketBuffer + pThreadInfo->nRecvPacketBufferOffset, nRecvBytes);
        pThreadInfo->nRecvPacketBufferOffset = nRecvBytes;
    }
    else
    {
        pThreadInfo->nRecvPacketBufferOffset += nRecvBytes;
    }


    /* TODO : 일단 일정이 급하므로 한번에 한패킷으로 해놓고 STX, ETX 검색 방식 수정 필요
     *         STX, ETX 검색도 이전 검색 결과를 저장해 놓고 추가 수신분에 대한 검색만 가능하게 해야 할 필요가 있음(처리 속도 문제)
     *         State machine 형태로 구현 필요
     *         기존 파싱 로직은 상태 머신 형태가 아니라 신규 구현 해야 함
     */
//    if (pThreadInfo->aRecvPacketBuffer[0] != SOCK_STX_CHAR)
//    {
//        WriteLog(LogType_StandardOut, LogLevel_Info, "Packet 데이터 오류 : 시작 문자가 STX가 아닙니다.[%s]", pThreadInfo->aRecvPacketBuffer);
//        pThreadInfo->nRecvPacketBufferOffset = 0;
//        pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
//        return SOCK_ERR_SUCCESS;
//    }
//    nSTXIndex = 0;
//
//    /* Packet이 완전히 수신되지 않은 경우 */
//    if (pThreadInfo->aRecvPacketBuffer[pThreadInfo->nRecvPacketBufferOffset - 1] != SOCK_ETX_CHAR)
//    {
//        pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
//        return SOCK_ERR_SUCCESS;
//    }
//    nETXIndex = pThreadInfo->nRecvPacketBufferOffset - 1;


    // 생각해보니 임시방편 다시 작성 - 문자열을 두번 검색함
//    nSTXIndex = 0;
//    while (pThreadInfo->aRecvPacketBuffer[nETXIndex] != SOCK_STX_CHAR && nETXIndex < pThreadInfo->nRecvPacketBufferOffset)
//    {
//        nSTXIndex++;
//    }
//    if (nSTXIndex == pThreadInfo->nRecvPacketBufferOffset)
//    {
//        // 데이터에 STX가 없으므로 버려도 되는 데이터임
//        pThreadInfo->nRecvPacketBufferOffset = 0;
//        pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
//        return SOCK_ERR_SUCCESS;
//    }
//
//    nETXIndex = nSTXIndex + 1;
//    while (pThreadInfo->aRecvPacketBuffer[nETXIndex] != SOCK_ETX_CHAR && nETXIndex < pThreadInfo->nRecvPacketBufferOffset)
//    {
//        nETXIndex++;
//    }
//
//    if (nETXIndex == pThreadInfo->nRecvPacketBufferOffset)
//    {
//        pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
//        return SOCK_ERR_SUCCESS;
//    }
    /*
     * STX, ETX 검색방법으로 변경
     */

    nSTXIndex = -1;
    nETXIndex = -1;
    for (nSearchIndex = 0; nSearchIndex < pThreadInfo->nRecvPacketBufferOffset; nSearchIndex++)
    {
        if (pThreadInfo->aRecvPacketBuffer[nSearchIndex] == SOCK_STX_CHAR)
        {
            nSTXIndex = nSearchIndex;
            continue;
        }
        if (pThreadInfo->aRecvPacketBuffer[nSearchIndex] == SOCK_ETX_CHAR)
        {
            nETXIndex = nSearchIndex;
            if (nSTXIndex != -1)
            {
                nChannelNo = pThreadInfo->aRecvPacketBuffer[nSTXIndex + 1] - '0';
                pMessageInfo = (struct MESSAGE_INFO *)NULL;
                nResult = SafeMemoryAlloc((void **)&pMessageInfo, sizeof(struct MESSAGE_INFO));
                if (nResult != STR_ERR_SUCCESS)
                {
                    WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "SafeMemoryAlloc Error[CODE:%d]", nResult);
                }

                SafeMemSet(pMessageInfo, sizeof(struct MESSAGE_INFO), 0, sizeof(struct MESSAGE_INFO));
                // malloc 사이즈는 문자열 마지막에 NULL 사이즈 포함
                pMessageInfo->nMessageLength = nETXIndex - nSTXIndex + 1;
                nResult = SafeMemoryAlloc((void **)&(pMessageInfo->paMessageData), nETXIndex - nSTXIndex + 2);
                //pMessageInfo->paMessageData = SafeAlloc(pMessageInfo->paMessageData, nETXIndex - nSTXIndex + 2);
                // nSTXIndex와 nETXIndex를 사용하는 이유는 추후 프로그램 수정 시 STX, ETX 검색 방식으로 변경 가능해서 미리 적용
            //    SafeMemCopy(pMessageInfo->paMessageData, nETXIndex - nSTXIndex + 1, pThreadInfo->aRecvPacketBuffer + nSTXIndex, nETXIndex - nSTXIndex + 1);
                SafeMemCopy(pMessageInfo->paMessageData, nETXIndex - nSTXIndex + 1, pThreadInfo->aRecvPacketBuffer + nSTXIndex, nETXIndex - nSTXIndex + 1);
                pMessageInfo->paMessageData[nETXIndex - nSTXIndex + 1] = '\0';

#ifdef _WIN32
                uuid_generate(&pMessageInfo->aUUID);
                uuid_unparse(&pMessageInfo->aUUID, szUUIDString);
#else
                uuid_generate(pMessageInfo->aUUID);
                uuid_unparse(pMessageInfo->aUUID, szUUIDString);
#endif
                // CRC 체크  루틴 추가
                nCRCCalculated = CalculateCRC16(0xFFFF, pMessageInfo->paMessageData + 1, nETXIndex - nSTXIndex + 1 - SOCK_PACKET_NONCRC_SIZE);

                nResult = SafeHexStringToInt(pThreadInfo->aRecvPacketBuffer + nETXIndex - 4, 4, &nCRCReceived);

                if (nCRCCalculated != nCRCReceived)
                {
                    SafeStrNCopy(szCRC, sizeof(szCRC), pThreadInfo->aRecvPacketBuffer + nETXIndex - 4, 4);
                    WriteLog(LogType_StandardOut, LogLevel_Info, "CRC Error[Received:%s, Calculated:%X, Data:%s]",szCRC, nCRCCalculated, pMessageInfo->paMessageData);
                    SafeFree((void **)&pMessageInfo->paMessageData);
                    SafeFree((void **)&pMessageInfo);
                    continue;
                }

                if (nResult != STR_ERR_SUCCESS)
                {
                    SafeStrNCopy(szCRC, sizeof(szCRC), pThreadInfo->aRecvPacketBuffer + nETXIndex - 4, 4);
                    WriteLog(LogType_StandardOut, LogLevel_Info, "CRC Error[Received:%s, Data:%s]",szCRC, pMessageInfo->paMessageData);
                    SafeFree((void **)&pMessageInfo->paMessageData);
                    SafeFree((void **)&pMessageInfo);
                    continue;
                }

                //void WriteCommLog(enum LogType eLogType, enum LogLevel eLogLevel, int nSocket, int nChannelNo, char cSendRecv, const char *szFormat, ...)
//                WriteCommLog(LogType_CommOut, LogLevel_Info, nSocket, nChannelNo, 'R', "%-*.*s,%s", UUID_STRING_SIZE, UUID_STRING_SIZE, szUUIDString, pMessageInfo->paMessageData);
                pthread_mutex_lock(&pThreadInfo->sChannelRecvMutex[nChannelNo]);
                nResult = DoubleLinkedListAddNodeToHead(&pThreadInfo->sChannelRecvFrameBuffer[nChannelNo], pMessageInfo, &pListNode);
                if (pListNode->pNodeData == NULL)
                {
                    printf("Recv Error!\n");
                }
                pthread_mutex_unlock(&pThreadInfo->sChannelRecvMutex[nChannelNo]);
                if (nResult != SOCK_ERR_SUCCESS)
                {
                    WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "DoubleLinkedListAddNodeToHead Error");
                    //pThreadInfo->nRecvPacketBufferOffset = 0;
                    // 버퍼정리
//                    memmove(pThreadInfo->aRecvPacketBuffer, pThreadInfo->aRecvPacketBuffer + nETXIndex + 1, pThreadInfo->nRecvPacketBufferOffset - nETXIndex + 1);
//                    pThreadInfo->nRecvPacketBufferOffset = pThreadInfo->nRecvPacketBufferOffset - (nETXIndex + 1);
//                    pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
//                    return SOCK_ERR_SUCCESS;
                }

                //pThreadInfo->nRecvPacketBufferOffset = 0;
                // 버퍼정리
                memmove(pThreadInfo->aRecvPacketBuffer, pThreadInfo->aRecvPacketBuffer + nETXIndex + 1, pThreadInfo->nRecvPacketBufferOffset - nETXIndex + 1);
                pThreadInfo->nRecvPacketBufferOffset = pThreadInfo->nRecvPacketBufferOffset - (nETXIndex + 1);
                nSearchIndex = -1;
                //pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
                //return SOCK_ERR_SUCCESS;
            }
            else  // ETX는 있는데 STX가 없는 경우 버림
            {

            }
        }
    }
    // STX 이전에 불필요한 데이터가 있을 경우 버림
    // 메모리 복제는 시간이 걸리므로 특정 용량 이상일 경우에만 버림
    if (nSTXIndex > 100 && nETXIndex == -1)
    {
        memmove(pThreadInfo->aRecvPacketBuffer, pThreadInfo->aRecvPacketBuffer + nSTXIndex + 1, pThreadInfo->nRecvPacketBufferOffset - nETXIndex + 1);
        pThreadInfo->nRecvPacketBufferOffset = pThreadInfo->nRecvPacketBufferOffset - (nETXIndex + 1);
    }


    // STX, ETX 체크 for문 안으로 이동
/*
    nChannelNo = pThreadInfo->aRecvPacketBuffer[nSTXIndex + 1] - '0';
    nResult = SafeMemoryAlloc((void **)&pMessageInfo, sizeof(struct MESSAGE_INFO));

    SafeMemSet(pMessageInfo, sizeof(struct MESSAGE_INFO), 0, sizeof(struct MESSAGE_INFO));
    // malloc 사이즈는 문자열 마지막에 NULL 사이즈 포함
    pMessageInfo->nMessageLength = nETXIndex - nSTXIndex + 1;
    nResult = SafeMemoryAlloc((void **)&pMessageInfo->paMessageData, nETXIndex - nSTXIndex + 2);
    //pMessageInfo->paMessageData = SafeAlloc(pMessageInfo->paMessageData, nETXIndex - nSTXIndex + 2);
    // nSTXIndex와 nETXIndex를 사용하는 이유는 추후 프로그램 수정 시 STX, ETX 검색 방식으로 변경 가능해서 미리 적용
//    SafeMemCopy(pMessageInfo->paMessageData, nETXIndex - nSTXIndex + 1, pThreadInfo->aRecvPacketBuffer + nSTXIndex, nETXIndex - nSTXIndex + 1);
    SafeMemCopy(pMessageInfo->paMessageData, nETXIndex - nSTXIndex + 1, pThreadInfo->aRecvPacketBuffer + nSTXIndex, nETXIndex - nSTXIndex + 1);
    pMessageInfo->paMessageData[nETXIndex - nSTXIndex + 1] = '\0';

    // CRC 체크  루틴 추가
    nCRCCalculated = CalculateCRC16(0xFFFF, pMessageInfo->paMessageData + 1, nETXIndex - nSTXIndex + 1 - SOCK_PACKET_NONCRC_SIZE);

    nResult = SafeHexStringToInt(pThreadInfo->aRecvPacketBuffer + nETXIndex - 4, 4, &nCRCReceived);

    if (nCRCCalculated != nCRCReceived)
    {
        SafeStrNCopy(szCRC, sizeof(szCRC), pThreadInfo->aRecvPacketBuffer + nETXIndex - 4, 4);
        WriteLog(LogType_StandardOut, LogLevel_Info, "CRC Error[Received:%s, Calculated:%X]",szCRC, nCRCCalculated);
    }

    if (nResult != STR_ERR_SUCCESS)
    {
        SafeStrNCopy(szCRC, sizeof(szCRC), pThreadInfo->aRecvPacketBuffer + nETXIndex - 4, 4);
        WriteLog(LogType_StandardOut, LogLevel_Info, "CRC Error[%s]",szCRC);
    }

    nResult = DoubleLinkedListAddNodeToHead(&pThreadInfo->sChannelRecvFrameBuffer[nChannelNo], pMessageInfo, &pListNode);
    if (nResult != SOCK_ERR_SUCCESS)
    {
        WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "DoubleLinkedListAddNodeToHead Error");
        //pThreadInfo->nRecvPacketBufferOffset = 0;
        // 버퍼정리
        memmove(pThreadInfo->aRecvPacketBuffer, pThreadInfo->aRecvPacketBuffer + nETXIndex + 1, pThreadInfo->nRecvPacketBufferOffset - nETXIndex + 1);
        pThreadInfo->nRecvPacketBufferOffset = pThreadInfo->nRecvPacketBufferOffset - (nETXIndex + 1);
        pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
        return SOCK_ERR_SUCCESS;
    }

    //pThreadInfo->nRecvPacketBufferOffset = 0;
    // 버퍼정리
    memmove(pThreadInfo->aRecvPacketBuffer, pThreadInfo->aRecvPacketBuffer + nETXIndex + 1, pThreadInfo->nRecvPacketBufferOffset - nETXIndex + 1);
    pThreadInfo->nRecvPacketBufferOffset = pThreadInfo->nRecvPacketBufferOffset - (nETXIndex + 1);
*/
    pthread_mutex_unlock(&(pThreadInfo->sRecvBufferMutex));
    return SOCK_ERR_SUCCESS;

}

int SendSocket(int nSocket, struct SOCKET_THREAD_INFO *pThreadInfo, bool *bConnected)
{

    int nChannelNo;
    int nProcessChannelCount;
    int nResult;
    //char aSendPacketBufferTemp[SOCK_BUFFER_SIZE + 1];
    bool bBufferDataExist;
    int nSendBytes;
    struct DOUBLELINKEDLISTNODE *pNode;
    struct MESSAGE_INFO *pMessageInfo;
    //int nPacketLength;

    pthread_mutex_lock(&(pThreadInfo->sSendBufferMutex));
    if (pThreadInfo->nSendPacketBufferOffset != 0)
    {
        nSendBytes = send(nSocket, pThreadInfo->aSendPacketBuffer, pThreadInfo->nSendPacketBufferOffset, 0);
        if (nSendBytes == -1)
        {
            // TODO : 오류 처리 루틴 확인
            switch (errno)
            {
            case EAGAIN:
                WriteLog(LogType_StandardOut, LogLevel_Info, "Socket send error[EAGAIN]");
                WriteTraceLog(LogType_StandardOut, LogLevel_Info, __FILE__, __LINE__, __FUNCTION__, "Socket send EAGAIN 발생");
                break;
            default:
                WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Socket send error:%s", strerror(errno));
            }
        }
        else
        {
            if (nSendBytes < pThreadInfo->nSendPacketBufferOffset)
            {
                memmove_s(pThreadInfo->aSendPacketBuffer, sizeof(pThreadInfo->aSendPacketBuffer), pThreadInfo->aSendPacketBuffer + nSendBytes, pThreadInfo->nSendPacketBufferOffset - nSendBytes);
                pThreadInfo->nSendPacketBufferOffset-= nSendBytes;
                pThreadInfo->aSendPacketBuffer[pThreadInfo->nSendPacketBufferOffset] = '\0';
            }
            else
            {
                pThreadInfo->nSendPacketBufferOffset = 0;
                pThreadInfo->nCurrentSendChannel = (pThreadInfo->nCurrentSendChannel + 1) % g_nChannelCount;
            }
        }
    }
    else
    {
        // TODO_(완료) : for 루프를 이전 처리 채널 이후 부터 전송 가능 하게 변경 필요.
        for (nChannelNo = pThreadInfo->nCurrentSendChannel, nProcessChannelCount = 0; nProcessChannelCount < g_nChannelCount; nChannelNo = (nChannelNo + 1) % g_nChannelCount, nProcessChannelCount++, pThreadInfo->nCurrentSendChannel = (pThreadInfo->nCurrentSendChannel + 1) % g_nChannelCount)
        {
            bBufferDataExist = false;
            nResult = DoubleLinkedListNodeExist(&pThreadInfo->sChannelSendFrameBuffer[nChannelNo], &bBufferDataExist);

            if (bBufferDataExist)
            {
                nResult = DoubleLinkedListRemoveTailNode(&pThreadInfo->sChannelSendFrameBuffer[nChannelNo], &pNode);
                if (nResult != DS_ERR_SUCCESS)
                {
                    // TODO : 오류 처리 추가(발생하면 심각한 오류이다. 화면 출력 내용 결정 필요)
                    WriteTraceLog(LogType_ErrorOut, LogLevel_Error, __FILE__, __LINE__, __FUNCTION__, "Double Linked List Remove Error!");
                    continue;
                }
                pMessageInfo = (struct MESSAGE_INFO *)pNode->pNodeData;
                //MakePacket(aSendPacketBufferTemp, (char *)(pNode->pNodeData), &nPacketLength);
                nSendBytes = send(nSocket, pMessageInfo->paMessageData, pMessageInfo->nMessageLength, 0);
                if (nSendBytes < pMessageInfo->nMessageLength)
                {
                    SafeMemCopy(pThreadInfo->aSendPacketBuffer, sizeof(pThreadInfo->aSendPacketBuffer), pMessageInfo->paMessageData + nSendBytes, pMessageInfo->nMessageLength - nSendBytes);
                    pThreadInfo->nSendPacketBufferOffset = pMessageInfo->nMessageLength - nSendBytes;
                    pThreadInfo->aSendPacketBuffer[pThreadInfo->nSendPacketBufferOffset] = '\0';
                    break;
                }
                WriteRawCommLog(LogType_CommOut, LogLevel_Info, nSocket, 'S', "%s", pMessageInfo->paMessageData);
            }
        }
    }
    //usleep(10000);
    pthread_mutex_unlock(&(pThreadInfo->sSendBufferMutex));
    return SOCK_ERR_SUCCESS;
}

int CheckConnection(int nSocket, bool *pbConnected)
{
    int nRecvBytes;

    //printf_s("check connection\n");
    if (*pbConnected == false)
    {
        //printf_s("already disconnected\n");
        return SOCK_ERR_SUCCESS;
    }
    if (nSocket == -1)
    {
        *pbConnected = false;
#ifdef _DEBUG
        printf_s("Invalid Socket\n");
#endif
        return SOCK_ERR_INVALID_SOCKET;
    }
    nRecvBytes = recv(nSocket, NULL, 0, 0);
    // 이미 종료되었다고 판단된 경우 체크하지 않는다.

    if (nRecvBytes == -1)
    {
#ifdef _WIN32
        switch (WSAGetLastError())
        {
        case WSAENOTCONN:
        case WSAENETRESET:
        case WSAECONNRESET:
        case WSAETIMEDOUT:
        case WSAECONNABORTED:
            *pbConnected = false;
            return SOCK_ERR_SUCCESS;

        case WSAENOTSOCK:
            *pbConnected = false;
            return SOCK_ERR_INVALID_SOCKET;

        case WSAEINPROGRESS:
        case WSAEWOULDBLOCK:
            *pbConnected = true;
            //printf_s("EAGAIN\n");
            return SOCK_ERR_SUCCESS;
        default:
            printf_s("default:%d\n", WSAGetLastError());
            break;
        }

#else
        switch (errno)
        {
        case ENOTCONN:
            *pbConnected = false;
            printf_s("ENOTCONN\n");
            return SOCK_ERR_SUCCESS;

        case ENOTSOCK:
            *pbConnected = false;
            printf_s("ENOTSOCK\n");
            return SOCK_ERR_INVALID_SOCKET;

        case EAGAIN:
            *pbConnected = true;
            //printf_s("EAGAIN\n");
            return SOCK_ERR_SUCCESS;
        default:
            printf_s("default:%d\n", errno);
            break;
        }
#endif
    }
    *pbConnected = true;
    return SOCK_ERR_SUCCESS;
}

/*
 * Thread를 Socket마다 하나를 할당할지? 아니면 채널별 할당할지? - 현재는
 * 현재 판단은 Socket 마다 하나면 충분할 것으로 판단됨
 * 너무 분할되어도 메모리 lock으로 크게 효용이 없을 것으로 판단됨
 * 채널 데이터 처리 프로세스에서 메모리 버퍼 주소는 어떻게 할것인지?(프로세스 변수로 전달하면 될것으로 판단됨)
 */
/*
void SocketProcessLoop(int nSocket)
{
    bool bConnected;
    int nResult;
    struct CHANNEL_BUFFER sCHANNEL_BUFFER;

    InitCHANNEL_BUFFER(&sCHANNEL_BUFFER);

    bConnected = true;

    while (g_bSocketProcessLoop)
    {
        bConnected = true;
        nResult = CheckConnection(nSocket, &bConnected);
        if (bConnected == false)
        {
            if (nResult == SOCK_ERR_SUCCESS && nSocket != -1)
            {
                close(nSocket);
                nSocket = -1;
            }
            nResult = SOCK_ERR_SUCCESS;
            switch (g_eConnectionType)
            {
            case SOCK_CONN_TYPE_SERVER:
                return;
            case SOCK_CONN_TYPE_CLIENT:
                nResult = ConnectTo(g_szConnectionIP, g_nServicePort, &nSocket);
                break;
            }
            if (nResult != SOCK_ERR_SUCCESS)
            {
                continue;
            }
        }

        RecvSocket(nSocket, &sCHANNEL_BUFFER, &bConnected);
        //ParsePacket();

        //GetRabbitMQ();
        SendSocket(nSocket, &sCHANNEL_BUFFER, &bConnected);

        // TODO : 프로세스 CPU 사용량 확인하고 재조정 필요
        usleep(1000);
    }
}
*/

/*
 * 싱글쓰레드용에서 쓰레드 구조체를 이용하게 변경
 */
void *SocketProcessLoop(void *pArg)
{
    struct SOCKET_THREAD_INFO *pThreadInfo;
    bool bConnected;
    int nResult;
    int nChannelNo;
    int nMessageLength;
    char aMessageBody[SOCK_BUFFER_SIZE + 1];
    uint64_t nDeliveryTag;
    int nCompareResult;
    char szUUIDString[UUID_STRING_SIZE + 1];

    char szResponseQueuename[MAX_STRING_SIZE + 1];
    //int nResponseQueuenameLength;
    //struct CHANNEL_BUFFER sCHANNEL_BUFFER;

    //InitCHANNEL_BUFFER(&sCHANNEL_BUFFER);
    int *pnReturnValue;
    char szExchangername[MAX_STRING_SIZE + 1];

    pThreadInfo = (struct SOCKET_THREAD_INFO *)pArg;
    switch (g_eConnectionType)
    {
    case SOCK_CONN_TYPE_SERVER:
        bConnected = true;
        break;
    case SOCK_CONN_TYPE_CLIENT:
        bConnected = false;
        break;
    }
    SetSocketBlockingEnabled(pThreadInfo->nSocket, false);
    pThreadInfo->nRunState = SOCK_THREAD_STATE_RUNNING;
    pnReturnValue = NULL;
    nResult = SafeMemoryAlloc((void **)&pnReturnValue, sizeof(int));
    //pnReturnValue = SafeAlloc(pnReturnValue, sizeof(int));
    *pnReturnValue = 0;
    WriteLog(LogType_SystemOut, LogLevel_Info, "Thread Started[No:%d]", pThreadInfo->nSocketIndex);
    //signal(SIGPIPE, SIG_IGN);

    g_fnInitializeFileTransferData(pThreadInfo->nSocketIndex);
    //while (pThreadInfo->bThreadLoop)
    while (GetSocketThreadLoopState(pThreadInfo))
    {
        nResult = CheckConnection(pThreadInfo->nSocket, &bConnected);
        if (bConnected == false)
        {
            if (nResult == SOCK_ERR_SUCCESS && pThreadInfo->nSocket != -1)
            {
                close(pThreadInfo->nSocket);
                pThreadInfo->nSocket = -1;
            }
            nResult = SOCK_ERR_SUCCESS;
            if (g_eConnectionType == SOCK_CONN_TYPE_SERVER)
            {
                pthread_mutex_lock(&pThreadInfo->sContextMutex);
                pThreadInfo->bThreadLoop = false;
                pthread_mutex_unlock(&pThreadInfo->sContextMutex);
                break;
            }
            else if (g_eConnectionType == SOCK_CONN_TYPE_CLIENT)
            {
                nResult = ConnectTo(g_szConnectionIP, g_nServicePort, &pThreadInfo->nSocket);
                g_fnInitializeFileTransferData(pThreadInfo->nSocketIndex);
                if (nResult != SOCK_ERR_SUCCESS)
                {
                    usleep(1000000);
                    continue;
                }
                bConnected = true;
            }
        }

        if (pThreadInfo->sPublishMQ.bMQConnection == false || pThreadInfo->sPublishMQ.bMQChannelOpen == false
            || pThreadInfo->sConsumeMQ.bMQConnection == false || pThreadInfo->sConsumeMQ.bMQChannelOpen == false)
        {
            if (ConnectSocketMQ(pThreadInfo) != MQ_ERR_SUCCESS)
            {
                continue;
            }
        }

        nResult = RecvSocket(pThreadInfo->nSocket, pThreadInfo, &bConnected);
        if (nResult == SOCK_ERR_CONNECT && g_eConnectionType == SOCK_CONN_TYPE_SERVER)
        {
            pthread_mutex_lock(&pThreadInfo->sContextMutex);
            close(pThreadInfo->nSocket);
            pThreadInfo->nSocket = -1;
            bConnected = false;
            pThreadInfo->bThreadLoop = false;
            pthread_mutex_unlock(&pThreadInfo->sContextMutex);
            continue;
        }
        //ParsePacket();

        //GetRabbitMQ();
        SendSocket(pThreadInfo->nSocket, pThreadInfo, &bConnected);

        for (nChannelNo = 0; nChannelNo < g_nChannelCount; nChannelNo++)
        {
            if (((g_nChannelTypeFlags[nChannelNo] & SOCK_CHANNEL_MESSAGE_SEND) != 0) || ((g_nChannelTypeFlags[nChannelNo] & SOCK_CHANNEL_MESSAGE_RECV) != 0))
            {
                if (g_fnProcessMessageSocketData != NULL)
                {
                    nResult = g_fnProcessMessageSocketData(pThreadInfo, nChannelNo);
                }

            }
            else if (((g_nChannelTypeFlags[nChannelNo] & SOCK_CHANNEL_FILE_SEND) != 0) || ((g_nChannelTypeFlags[nChannelNo] & SOCK_CHANNEL_FILE_RECV) != 0))
            {
                if (g_fnProcessFileSocketData != NULL)
                {
                    nResult = g_fnProcessFileSocketData(pThreadInfo, nChannelNo);
                }
            }
        }

        amqp_maybe_release_buffers(pThreadInfo->sConsumeMQ.sMQConnectionState);
        if (amqp_frames_enqueued(pThreadInfo->sConsumeMQ.sMQConnectionState) || amqp_data_in_buffer(pThreadInfo->sConsumeMQ.sMQConnectionState)
            || MQReadCheck(pThreadInfo->sConsumeMQ.sMQConnectionState))
        {
            SafeMemSet(szExchangername, sizeof(szExchangername), 0, sizeof(szExchangername));
            nResult = GetMessageFromQueue(pThreadInfo->sConsumeMQ.sMQConnectionState, szExchangername, sizeof(szExchangername), aMessageBody, sizeof(aMessageBody), szUUIDString, sizeof(szUUIDString), szResponseQueuename, sizeof(szResponseQueuename), &pThreadInfo->sConsumeMQ.bMQConnection, &pThreadInfo->sConsumeMQ.bMQChannelOpen, &nDeliveryTag, (struct timeval *)NULL);
            if (nResult == MQ_ERR_SUCCESS)
            {
                nMessageLength = strnlen(aMessageBody, sizeof(aMessageBody));
                nResult = SafeStrCmp(szExchangername, sizeof(szExchangername), g_szMQFileSendExchangername, &nCompareResult);
                if (nCompareResult == 0)
                {
                    nChannelNo = GetChannelNo(SOCK_CHANNEL_FILE_SEND);
                    nResult = g_fnProcessFileMQData(pThreadInfo, nChannelNo, aMessageBody, nMessageLength, szUUIDString, sizeof(szUUIDString), szResponseQueuename, sizeof(szResponseQueuename), &pThreadInfo->sConsumeMQ.bMQConnection, &pThreadInfo->sConsumeMQ.bMQChannelOpen, nDeliveryTag);
                }
                else
                {
                    nResult = SafeStrCmp(szExchangername, sizeof(szExchangername), g_szMQMessageSendExchangername, &nCompareResult);
                    if (nCompareResult == 0)
                    {
                        nChannelNo = GetChannelNo(SOCK_CHANNEL_MESSAGE_SEND);
                        //WriteLog(LogType_StandardOut, LogLevel_Info, "Execute g_fnProcessMessageMQSendData");
                        nResult = g_fnProcessMessageMQSendData(pThreadInfo, nChannelNo, aMessageBody, nMessageLength, szUUIDString, sizeof(szUUIDString), szResponseQueuename, sizeof(szResponseQueuename), &pThreadInfo->sConsumeMQ.bMQConnection, &pThreadInfo->sConsumeMQ.bMQChannelOpen, nDeliveryTag);
//                        amqp_basic_ack(pThreadInfo->sConsumeMQ.sMQConnectionState, 1, nDeliveryTag, 0);
//                        WriteQueueLog(LogType_MQOut, LogLevel_Info, pThreadInfo->nSocket, 'C', g_szMQMessageSendQueuename, "", szUUIDString, nMessageLength, "%s", aMessageBody);
//                        SendMessage(pThreadInfo, nChannelNo, aMessageBody, nMessageLength, szUUIDString, sizeof(szUUIDString), szResponseQueuename, sizeof(szResponseQueuename));
                    }
                    else
                    {
                        nChannelNo = GetChannelNo(SOCK_CHANNEL_MESSAGE_RECV);
                        //WriteLog(LogType_StandardOut, LogLevel_Info, "Execute g_fnProcessMessageMQRespData");
                        nResult = g_fnProcessMessageMQRespData(pThreadInfo, nChannelNo, aMessageBody, nMessageLength, szUUIDString, sizeof(szUUIDString), szResponseQueuename, sizeof(szResponseQueuename), &pThreadInfo->sConsumeMQ.bMQConnection, &pThreadInfo->sConsumeMQ.bMQChannelOpen, nDeliveryTag);
//                        amqp_basic_ack(pThreadInfo->sConsumeMQ.sMQConnectionState, 1, nDeliveryTag, 0);
//                        WriteQueueLog(LogType_MQOut, LogLevel_Info, pThreadInfo->nSocket, 'C', g_szMQMessageSendExchangername, "", szUUIDString, nMessageLength, "%s", aMessageBody);
//                        ProcessRecvQueueResponse(pThreadInfo, nChannelNo, aMessageBody, sizeof(aMessageBody), szUUIDString, sizeof(szUUIDString));
                    }
                }
            }
            else
            {
                WriteTraceLog(LogType_ErrorOut, LogLevel_Debug, __FILE__, __LINE__, __FUNCTION__, "GetMessageFromQueue Error!");
                amqp_basic_nack(pThreadInfo->sConsumeMQ.sMQConnectionState, 1, nDeliveryTag, 0, 1);
            }
        }


        /*
         * TODO : 동기화 모드일때 타임아웃 체크 루틴 추가 필요
         */


        /* TODO : 프로세스 CPU 사용량 확인하고 재조정 필요
         *         usleep 보다 select로 변경 필요.(일단 usleep 설계가 미완료)
         *         socket에서 가장 많이 사용되는 모드는 select + non-block
         *         Main Thread에서 Select를 이용하여 이벤트를 생성하고 각 쓰레드 에서는 이벤트가 있는 경우 처리하게 변경 할 필요가 있음
         */

        usleep(1000);

    }
    pthread_mutex_lock(&g_sChannelMQThreadInfoMutex);
    g_pChannelMQThreadInfo[pThreadInfo->nSocketIndex] = (struct SOCKET_THREAD_INFO *)NULL;
    pthread_mutex_unlock(&g_sChannelMQThreadInfoMutex);
    CloseMQ(pThreadInfo->nSocketIndex, pThreadInfo->sPublishMQ.sMQConnectionState, &pThreadInfo->sPublishMQ.bMQConnection, &pThreadInfo->sPublishMQ.bMQChannelOpen, &pThreadInfo->sPublishMQ.sMQMutex);
    CloseMQ(pThreadInfo->nSocketIndex, pThreadInfo->sConsumeMQ.sMQConnectionState, &pThreadInfo->sConsumeMQ.bMQConnection, &pThreadInfo->sConsumeMQ.bMQChannelOpen, &pThreadInfo->sConsumeMQ.sMQMutex);
    pthread_mutex_lock(&pThreadInfo->sContextMutex);
    pThreadInfo->nRunState = SOCK_THREAD_STATE_TERMINATED;
    pthread_mutex_unlock(&pThreadInfo->sContextMutex);
    WriteLog(LogType_StandardOut, LogLevel_Info, "Connection Closed![IP : %s, Port : %d]", pThreadInfo->szRemoteIP, pThreadInfo->nRemotePort);
    //pThreadInfo->nRunState = SOCK_THREAD_STATE_TERMINATED;
    WriteLog(LogType_SystemOut, LogLevel_Info, "Thread Stopped[No:%d]", pThreadInfo->nSocketIndex);

    pthread_exit(pnReturnValue);
    return NULL;

}
void InitCHANNEL_BUFFER(struct SOCKET_THREAD_INFO *pThreadInfo)
{
    int nChannelNo;

    pThreadInfo->sPublishMQ.bMQConnection = false;
    pThreadInfo->sPublishMQ.bMQChannelOpen = false;
    pThreadInfo->sConsumeMQ.bMQConnection = false;
    pThreadInfo->sConsumeMQ.bMQChannelOpen = false;
    SafeMemSet(pThreadInfo->aRecvPacketBuffer, sizeof(pThreadInfo->aRecvPacketBuffer), 0, sizeof(pThreadInfo->aRecvPacketBuffer));
    SafeMemSet(pThreadInfo->aSendPacketBuffer, sizeof(pThreadInfo->aSendPacketBuffer), 0, sizeof(pThreadInfo->aSendPacketBuffer));
    pThreadInfo->nRecvPacketBufferOffset = 0;
    pThreadInfo->nSendPacketBufferOffset = 0;
    pthread_mutex_init(&pThreadInfo->sRecvBufferMutex, NULL);
    pthread_mutex_init(&pThreadInfo->sSendBufferMutex, NULL);

    //pCHANNEL_BUFFER->sRecvBufferMutex = PTHREAD_MUTEX_INITIALIZER;
    //pCHANNEL_BUFFER->sSendBufferMutex = PTHREAD_MUTEX_INITIALIZER;

    for (nChannelNo = 0; nChannelNo < SOCK_CONF_MAX_CHANNEL_LIMIT; nChannelNo++)
    {
        InitDoubleLinkedList(&pThreadInfo->sChannelRecvFrameBuffer[nChannelNo]);
        InitDoubleLinkedList(&pThreadInfo->sChannelSendFrameBuffer[nChannelNo]);
        InitDoubleLinkedList(&pThreadInfo->sChannelRecvResponseBuffer[nChannelNo]);
        InitDoubleLinkedList(&pThreadInfo->sChannelSendResponseBuffer[nChannelNo]);
        pthread_mutex_init(&pThreadInfo->sChannelRecvMutex[nChannelNo], NULL);
        pthread_mutex_init(&pThreadInfo->sChannelSendMutex[nChannelNo], NULL);
        pthread_mutex_init(&pThreadInfo->sChannelRecvResponseMutex[nChannelNo], NULL);
        pthread_mutex_init(&pThreadInfo->sChannelSendResponseMutex[nChannelNo], NULL);
        SafeMemSet(&pThreadInfo->sLastRecvTime[nChannelNo], sizeof(pThreadInfo->sLastRecvTime[nChannelNo]), 0, sizeof(pThreadInfo->sLastRecvTime[nChannelNo]));
        SafeMemSet(&pThreadInfo->sLastSendTime[nChannelNo], sizeof(pThreadInfo->sLastSendTime[nChannelNo]), 0, sizeof(pThreadInfo->sLastSendTime[nChannelNo]));
        pThreadInfo->nLastRecvTimeMilliSec[nChannelNo] = 0;
        pThreadInfo->nLastSendTimeMilliSec[nChannelNo] = 0;
    }
    pThreadInfo->nCurrentSendChannel = 0;
}

void DestroyCHANNEL_BUFFER(struct SOCKET_THREAD_INFO *pThreadInfo)
{
    int nChannelNo;

    //SafeFree(&pThreadInfo->aRecvPacketBuffer);
    //SafeFree(&pThreadInfo->aSendPacketBuffer);
    pthread_mutex_destroy(&pThreadInfo->sRecvBufferMutex);
    pthread_mutex_destroy(&pThreadInfo->sSendBufferMutex);

    //pCHANNEL_BUFFER->sRecvBufferMutex = PTHREAD_MUTEX_INITIALIZER;
    //pCHANNEL_BUFFER->sSendBufferMutex = PTHREAD_MUTEX_INITIALIZER;

    for (nChannelNo = 0; nChannelNo < SOCK_CONF_MAX_CHANNEL_LIMIT; nChannelNo++)
    {
        DoubleLinkedListClear(&pThreadInfo->sChannelRecvFrameBuffer[nChannelNo]);
        DoubleLinkedListClear(&pThreadInfo->sChannelSendFrameBuffer[nChannelNo]);
        DoubleLinkedListClear(&pThreadInfo->sChannelRecvResponseBuffer[nChannelNo]);
        DoubleLinkedListClear(&pThreadInfo->sChannelSendResponseBuffer[nChannelNo]);
        pthread_mutex_destroy(&pThreadInfo->sChannelRecvMutex[nChannelNo]);
        pthread_mutex_destroy(&pThreadInfo->sChannelSendMutex[nChannelNo]);
        pthread_mutex_destroy(&pThreadInfo->sChannelRecvResponseMutex[nChannelNo]);
        pthread_mutex_destroy(&pThreadInfo->sChannelSendResponseMutex[nChannelNo]);
        //SafeFree(&pThreadInfo->sLastRecvTime[nChannelNo]);
        //SafeFree(&pThreadInfo->sLastSendTime[nChannelNo]);
    }
}
/*
struct SOCKET_THREAD_INFO
{
    bool bThreadLoop;
    int nRunState;
    pthread_mutex_t sContextMutex;
    pthread_t nThreadID;
    int nSocket;
    int nSocketIndex;
    amqp_socket_t *psMQSocket;
    amqp_connection_state_t sMQConnectionState;
    bool bMQConnection;
    bool bChannelOpen;
    pthread_mutex_t sMessageQueueMutex;

    // CHANNEL_BUFFER -> THREAD_INFO
    char aRecvPacketBuffer[SOCK_BUFFER_SIZE + 1];
    char aSendPacketBuffer[SOCK_BUFFER_SIZE + 1];
    int nRecvPacketBufferOffset;
    int nSendPacketBufferOffset;
    pthread_mutex_t sRecvBufferMutex;
    pthread_mutex_t sSendBufferMutex;
    struct DOUBLELINKEDLISTINFO sChannelRecvFrameBuffer[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct DOUBLELINKEDLISTINFO sChannelSendFrameBuffer[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct DOUBLELINKEDLISTINFO sChannelRecvResponseBuffer[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct DOUBLELINKEDLISTINFO sChannelSendResponseBuffer[SOCK_CONF_MAX_CHANNEL_LIMIT];

    pthread_mutex_t sChannelRecvMutex[SOCK_CONF_MAX_CHANNEL_LIMIT];
    pthread_mutex_t sChannelSendMutex[SOCK_CONF_MAX_CHANNEL_LIMIT];
    pthread_mutex_t sChannelRecvResponseMutex[SOCK_CONF_MAX_CHANNEL_LIMIT];
    pthread_mutex_t sChannelSendResponseMutex[SOCK_CONF_MAX_CHANNEL_LIMIT];
    int nCurrentSendChannel;
    void (*FrameParsingFunction[SOCK_CONF_MAX_CHANNEL_LIMIT])(struct DOUBLELINKEDLISTINFO);
    int nChannelState[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct tm sLastRecvTime[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct tm sLastSendTime[SOCK_CONF_MAX_CHANNEL_LIMIT];
    int nLastRecvTimeMilliSec[SOCK_CONF_MAX_CHANNEL_LIMIT];
    int nLastSendTimeMilliSec[SOCK_CONF_MAX_CHANNEL_LIMIT];
    char szRemoteIP[MAX_STRING_SIZE + 1];
    int nRemotePort;
    //char szRecvReplayQueueName[MAX_STRING_SIZE + 1];
};
*/
/******************************************************************************
*
*   Function Name   : CalculateCRC16
*   Desc.           : 1. CRC16 Lookup 테이블을 이용하여 CRC16값을 계산
*                     2. Polyniminal 변경이 필요한 경우 해당 Polynominal로 구성된 lookup table로 변경한다.
*   Input           : nSeed - CRC 계산 초기값(일반적으로 0xFFFF를 사용)
*                     pvData - CRC 계산을 위한 데이터 point
*                     nLength - 데이터의 사이즈
*   Output          : CRC16 계산값
*   Rev. Info.      : 1.0 - 최초작성(2018.08.29 : Kim Dalhyun) - 공개 소스 사용
*
******************************************************************************/
uint16_t CalculateCRC16(uint16_t nSeed, void *pvData, size_t nLength)
{
    char *pData = pvData;
    uint16_t nCRC16 = nSeed;

    while (nLength--)
    {
        nCRC16 = (nCRC16 << 8) ^ CRC16Table[((nCRC16 >> 8) ^ *pData++)];
    }

    return nCRC16;
}

/******************************************************************************
*
*   Function Name   : CalculateCRC32
*   Desc.           : 1. CRC32 Lookup 테이블을 이용하여 CRC32값을 계산
*                     2.Polyniminal 변경이 필요한 경우 해당 Polynominal로 구성된 lookup table로 변경한다.
*   Input           : nSeed - CRC 계산 초기값(일반적으로 0xFFFF를 사용)
*                     pvData - CRC 계산을 위한 데이터 point
*                     nLength - 데이터의 사이즈
*   Output          : CRC32 계산값
*   Rev. Info.      : 1.0 - 최초작성(2018.08.29 : Kim Dalhyun) - MIT 공개 소스 사용
*
******************************************************************************/
uint32_t CalculateCRC32(uint32_t nSeed, void *pvData, size_t nLength)
{
    char *pData = pvData;
    uint32_t nCRC32 = nSeed;


    while (nLength--)
    {
        nCRC32 = CRC32Table[(nCRC32 ^ *pData++) & 0xFF] ^ (nCRC32 >> 8);
    }
    return nCRC32 ^ ~0U;
}

bool SetSocketBlockingEnabled(int nSocket, bool bBlocking)
{
#ifdef _WIN32

   unsigned long nMode;

#else

   int nFlags;

#endif

   if (nSocket < 0)
   {
       return false;
   }

#ifdef _WIN32

   nMode = bBlocking ? 0 : 1;

   return (ioctlsocket(nSocket, FIONBIO, &nMode) == 0) ? true : false;

#else

   nFlags = fcntl(nSocket, F_GETFL, 0);

   if (nFlags == -1)
   {
       return false;
   }
   nFlags = bBlocking ? (nFlags & ~O_NONBLOCK) : (nFlags | O_NONBLOCK);
   return fcntl(nSocket, F_SETFL, nFlags) != -1;

#endif
}

int ServerMQChannelAllocation(struct SOCKET_THREAD_INFO *pThreadInfo, int *pnChannelNo)
{
    int i;
    pthread_mutex_lock(&g_sChannelMQThreadInfoMutex);

    for (i = 0; i < g_nMaxConnections; i++)
    {
        if (g_pChannelMQThreadInfo[i] == NULL)
        {
            g_pChannelMQThreadInfo[i] = pThreadInfo;
            *pnChannelNo = i;
            pthread_mutex_unlock(&g_sChannelMQThreadInfoMutex);
            return SOCK_ERR_SUCCESS;
        }
    }
    pthread_mutex_unlock(&g_sChannelMQThreadInfoMutex);
    return SOCK_ERR_MQ_CHANNEL_FULL;

}
int MakePacket(int nChannelNo, const char *pszFrameData, int nFrameLen, char *pszPacket, int *nPacketLen)
{
    int nOffset = 0;
    uint16_t nCRCCalculated;
    int nResult;

    pszPacket[nOffset++] = SOCK_STX_CHAR;
    pszPacket[nOffset++] = (char)(nChannelNo + '0');
    //snprintf(pszPacket + nOffset, *nPacketLen - nOffset, "%0*.*X", SOCK_PACKET_LEN_MSG_LEN, SOCK_PACKET_LEN_MSG_LEN, nFrameLen);
    //nOffset += SOCK_PACKET_LEN_MSG_LEN;
    nResult = SafeMemCopy(pszPacket + nOffset, *nPacketLen - nOffset, pszFrameData, nFrameLen);
    if (nResult != SEC_ERR_SUCCESS)
    {
        return SOCK_ERR_MAKE_DATA;
    }
    nOffset += nFrameLen;
    //printf_s("CRCLength : %d, %04X", nFrameLen + SOCK_PACKET_LEN_CHANNEL_NO, nCRCCalculated);
    nCRCCalculated = CalculateCRC16(0xFFFF, pszPacket + 1, nFrameLen + SOCK_PACKET_LEN_CHANNEL_NO);
    snprintf(pszPacket + nOffset, *nPacketLen - nOffset, "%0*X", SOCK_CRC_LENGTH, nCRCCalculated);
    nOffset += SOCK_CRC_LENGTH;
    pszPacket[nOffset++] = SOCK_ETX_CHAR;
    pszPacket[nOffset] = '\0';

    *nPacketLen = nOffset;

    return SOCK_ERR_SUCCESS;
}

int MakeFileFrame(char *pszOrganizationID, int nOrganizationIDLen, char cOccurType, char *pszWorkType, int nWorkTypeLen, char *pszMsgType, int nMsgTypeLength, char *pszFilename, int nFilenameLen, char *pszRequestDTime, int nRequestDTimeLen, char *pszResponseCode, int nResponseCodeLen, char *pData, int nDataLen, char *pszFrameData, int *nFrameDataLen)
{

    int nOffset = 0;
    int nResult;
    char szStringBuffer[MAX_STRING_SIZE + 1];


    snprintf(szStringBuffer, sizeof(szStringBuffer), "%0*X", SOCK_PACKET_LEN_MSG_LEN, nDataLen + SOCK_FRAME_SIZE_FILE);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_PACKET_LEN_MSG_LEN);
    nOffset += SOCK_PACKET_LEN_MSG_LEN;

    nResult = CheckNULL(pszOrganizationID, nOrganizationIDLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszOrganizationID[nOrganizationIDLen] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_ORG, SOCK_FRAME_LEN_ORG, pszOrganizationID);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_ORG);
    nOffset += SOCK_FRAME_LEN_ORG;

    pszFrameData[nOffset] = cOccurType;
    nOffset += SOCK_FRAME_LEN_OCCUR_TYPE;

    nResult = CheckNULL(pszWorkType, nWorkTypeLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszWorkType[nWorkTypeLen - 1] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_WORK_TYPE, SOCK_FRAME_LEN_WORK_TYPE, pszWorkType);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_WORK_TYPE);
    nOffset += SOCK_FRAME_LEN_WORK_TYPE;

    nResult = CheckNULL(pszMsgType, nMsgTypeLength);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszMsgType[nMsgTypeLength - 1] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_MSG_TYPE, SOCK_FRAME_LEN_MSG_TYPE, pszMsgType);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_MSG_TYPE);
    nOffset += SOCK_FRAME_LEN_MSG_TYPE;

    nResult = CheckNULL(pszFilename, nFilenameLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszFilename[nFilenameLen - 1] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_FILENAME, SOCK_FRAME_LEN_FILENAME, pszFilename);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_FILENAME);
    nOffset += SOCK_FRAME_LEN_FILENAME;

    nResult = CheckNULL(pszRequestDTime, nRequestDTimeLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszRequestDTime[nRequestDTimeLen - 1] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_REQUEST_DTIME, SOCK_FRAME_LEN_REQUEST_DTIME, pszRequestDTime);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_REQUEST_DTIME);
    nOffset += SOCK_FRAME_LEN_REQUEST_DTIME;

    nResult = CheckNULL(pszResponseCode, nResponseCodeLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszResponseCode[nResponseCodeLen - 1] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_RESP_CODE, SOCK_FRAME_LEN_RESP_CODE, pszResponseCode);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_RESP_CODE);
    nOffset += SOCK_FRAME_LEN_RESP_CODE;

    if (pData != NULL)
    {
        nResult = CheckNULL(pData, nDataLen);
        if (nResult != STR_ERR_SUCCESS)
        {
            pData[nDataLen] = '\0';
        }
        SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, pData, nDataLen);
        nOffset += nDataLen;
    }
    pszFrameData[nOffset] = '\0';
    *nFrameDataLen = nOffset;


    return SOCK_ERR_SUCCESS;
}


int MakeMSGFrame(char *pszOrganizationID, int nOrganizationIDLen, char cOccurType, char *pszWorkType, int nWorkTypeLen, char *pszMsgType, int nMsgTypeLength, char *pszSerialID, int nSerialIDLen, char *pszRequestDTime, int nRequestDTimeLen, char *pszResponseCode, int nResponseCodeLen, char *pData, int nDataLen, char *pszFrameData, int *nFrameDataLen)
{
    int nOffset = 0;
    int nResult;
    char szStringBuffer[MAX_STRING_SIZE + 1];


    snprintf(szStringBuffer + nOffset, sizeof(szStringBuffer), "%0*X", SOCK_PACKET_LEN_MSG_LEN, nDataLen + SOCK_FRAME_SIZE_MSG);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_PACKET_LEN_MSG_LEN);
    nOffset += SOCK_PACKET_LEN_MSG_LEN;

    nResult = CheckNULL(pszOrganizationID, nOrganizationIDLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszOrganizationID[nOrganizationIDLen] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_ORG, SOCK_FRAME_LEN_ORG, pszOrganizationID);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_ORG);
    nOffset += SOCK_FRAME_LEN_ORG;

    pszFrameData[nOffset] = cOccurType;
    nOffset += SOCK_FRAME_LEN_OCCUR_TYPE;

    nResult = CheckNULL(pszWorkType, nWorkTypeLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszWorkType[nWorkTypeLen] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_WORK_TYPE, SOCK_FRAME_LEN_WORK_TYPE, pszWorkType);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_WORK_TYPE);
    nOffset += SOCK_FRAME_LEN_WORK_TYPE;

    nResult = CheckNULL(pszMsgType, nMsgTypeLength);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszMsgType[nMsgTypeLength] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_MSG_TYPE, SOCK_FRAME_LEN_MSG_TYPE, pszMsgType);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_MSG_TYPE);
    nOffset += SOCK_FRAME_LEN_MSG_TYPE;

    nResult = CheckNULL(pszSerialID, nSerialIDLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszSerialID[nSerialIDLen] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_SERIAL_NO, SOCK_FRAME_LEN_SERIAL_NO, pszSerialID);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_SERIAL_NO);
    nOffset += SOCK_FRAME_LEN_SERIAL_NO;

    nResult = CheckNULL(pszRequestDTime, nRequestDTimeLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszRequestDTime[nRequestDTimeLen] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_REQUEST_DTIME, SOCK_FRAME_LEN_REQUEST_DTIME, pszRequestDTime);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_REQUEST_DTIME);
    nOffset += SOCK_FRAME_LEN_REQUEST_DTIME;

    nResult = CheckNULL(pszResponseCode, nResponseCodeLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pszResponseCode[nResponseCodeLen] = '\0';
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%-*.*s", SOCK_FRAME_LEN_RESP_CODE, SOCK_FRAME_LEN_RESP_CODE, pszResponseCode);
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, szStringBuffer, SOCK_FRAME_LEN_RESP_CODE);
    nOffset += SOCK_FRAME_LEN_RESP_CODE;

    nResult = CheckNULL(pData, nDataLen);
    if (nResult != STR_ERR_SUCCESS)
    {
        pData[nDataLen] = '\0';
    }
    SafeMemCopy(pszFrameData + nOffset, *nFrameDataLen - nOffset, pData, nDataLen);
    nOffset += nDataLen;
    pszFrameData[nOffset] = '\0';
    *nFrameDataLen = nOffset;

    return SOCK_ERR_SUCCESS;
}

void ProcessSendTimeout(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo)
{
    struct MESSAGE_RESPONSE_INFO *pResponseInfo;
    //struct MESSAGE_INFO *pMessageInfo;
    struct DOUBLELINKEDLISTNODE *pNode;

    struct timespec sTimeSpec;
    time_t sNow;
    struct tm pNow;
    int nMillisecond;
    int nTimeDiff;
    int nResult;
    //int nPacketLength;
    //char aFrameBuffer[SOCK_BUFFER_SIZE + 1];
    //char aPacketBuffer[SOCK_BUFFER_SIZE + 1];
    char szUUIDStringTemp[UUID_STRING_SIZE + 1];

    pResponseInfo = NULL;
    pNode = NULL;
    nResult = DoubleLinkedListMoveToHead(&pThreadInfo->sChannelSendResponseBuffer[nChannelNo], &pNode);

    while (nResult == DS_ERR_SUCCESS)
    {
        pResponseInfo = (struct MESSAGE_RESPONSE_INFO *)pNode->pNodeData;
        // 정상 응답은 통신 완료 되었으므로 리스트에서 삭제
        // timeout이 발생한 경우 최초수신측에 Timeout 응답을 전송하고 메모리에 보관
        // timeout이 발생한 전문의 내부 응답을 수신하였을 경우 오류 로그 저장하고 메모리에서 삭제
        clock_gettime(CLOCK_REALTIME, &sTimeSpec);
        sNow = sTimeSpec.tv_sec;
        nMillisecond = sTimeSpec.tv_nsec / 1.0e6; // nanoseconds to milliseconds
        localtime_r(&sNow, &pNow);
        nTimeDiff = TimeDiff(&pResponseInfo->sTransferTime, pResponseInfo->nTransferMilliSecond, &pNow, nMillisecond);
#ifdef _WIN32
        uuid_unparse(&pResponseInfo->aUUID, szUUIDStringTemp);
#else
        uuid_unparse(pResponseInfo->aUUID, szUUIDStringTemp);
#endif
        szUUIDStringTemp[UUID_STRING_SIZE] = '\0';

        // 정상 응답이 된 전문에 대해서는 리스트에서 삭제
        if (pResponseInfo->bResponse == true && pResponseInfo->bTimeout == false)
        {
            WriteLog(LogType_StandardOut, LogLevel_Debug, "Delete Sended Message[UUID:%s]", szUUIDStringTemp);
            nResult = DoubleLinkedListRemoveCurrentNode(&pThreadInfo->sChannelSendResponseBuffer[nChannelNo], &pNode);
            free(pResponseInfo);
            free(pNode);
            pResponseInfo = NULL;
            pNode = NULL;
            nResult = DoubleLinkedListGetCurrentNode(&pThreadInfo->sChannelSendResponseBuffer[nChannelNo], &pNode);
            continue;
        }

        // 응답이 전송되지 않았으며 시간초과된 경우
        if (pResponseInfo->bResponse == false && nTimeDiff >= g_nErrorTimeout)// && g_bUseGWTimeout == true)
        {
            WriteLog(LogType_StandardOut, LogLevel_Debug, "Delete Send Timeouted Message[UUID:%s]", szUUIDStringTemp);
            // 타임아웃 응답 코드 전송 기능 추가
            if (!g_bBypassMode)
            {
                amqp_basic_nack(pThreadInfo->sConsumeMQ.sMQConnectionState, 1, pResponseInfo->nDeliveryTag, 0, 1);
            }

            pResponseInfo->bResponse = true;
            pResponseInfo->bTimeout = true;
        }

        if (pResponseInfo->bResponse == true && pResponseInfo->bTimeout == true && pResponseInfo->bResponseRecv == false && nTimeDiff >= g_nTimeoutStoreTerm)
        {
            WriteLog(LogType_StandardOut, LogLevel_Debug, "Delete Send Dropped Message[UUID:%s]", szUUIDStringTemp);
            nResult = DoubleLinkedListRemoveCurrentNode(&pThreadInfo->sChannelSendResponseBuffer[nChannelNo], &pNode);
            free(pResponseInfo);
            free(pNode);
            pResponseInfo = NULL;
            pNode = NULL;
            nResult = DoubleLinkedListGetCurrentNode(&pThreadInfo->sChannelSendResponseBuffer[nChannelNo], &pNode);
            continue;
        }

        nResult = DoubleLinkedListMoveToNext(&pThreadInfo->sChannelSendResponseBuffer[nChannelNo], &pNode);
    }

}

void ProcessResponseTimeout(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo)
{
    struct MESSAGE_RESPONSE_INFO *pResponseInfo;
    struct MESSAGE_INFO *pMessageInfo;
    struct DOUBLELINKEDLISTNODE *pNode;

    struct timespec sTimeSpec;
    time_t sNow;
    struct tm pNow;
    int nMillisecond;
    int nTimeDiff;
    int nResult;
    int nPacketLength;
    char aFrameBuffer[SOCK_BUFFER_SIZE + 1];
    char aPacketBuffer[SOCK_BUFFER_SIZE + 1];
    char szUUIDStringTemp[UUID_STRING_SIZE + 1];

    pResponseInfo = NULL;
    pNode = NULL;
    nResult = DoubleLinkedListMoveToHead(&pThreadInfo->sChannelRecvResponseBuffer[nChannelNo], &pNode);

    while (nResult == DS_ERR_SUCCESS)
    {
        pResponseInfo = (struct MESSAGE_RESPONSE_INFO *)pNode->pNodeData;
        // 정상 응답은 통신 완료 되었으므로 리스트에서 삭제
        // timeout이 발생한 경우 최초수신측에 Timeout 응답을 전송하고 메모리에 보관
        // timeout이 발생한 전문의 내부 응답을 수신하였을 경우 오류 로그 저장하고 메모리에서 삭제
        clock_gettime(CLOCK_REALTIME, &sTimeSpec);
        sNow = sTimeSpec.tv_sec;
        nMillisecond = sTimeSpec.tv_nsec / 1.0e6; // nanoseconds to milliseconds
        localtime_r(&sNow, &pNow);
        nTimeDiff = TimeDiff(&pResponseInfo->sTransferTime, pResponseInfo->nTransferMilliSecond, &pNow, nMillisecond);
#ifdef _WIN32
        uuid_unparse(&pResponseInfo->aUUID, szUUIDStringTemp);
#else
        uuid_unparse(pResponseInfo->aUUID, szUUIDStringTemp);
#endif
        szUUIDStringTemp[UUID_STRING_SIZE] = '\0';

        // 정상 응답이 된 전문에 대해서는 리스트에서 삭제
        if (pResponseInfo->bResponse == true && pResponseInfo->bTimeout == false)
        {
            WriteLog(LogType_StandardOut, LogLevel_Debug, "Delete Responsed Message[UUID:%s]", szUUIDStringTemp);
            nResult = DoubleLinkedListRemoveCurrentNode(&pThreadInfo->sChannelRecvResponseBuffer[nChannelNo], &pNode);
            free(pResponseInfo);
            free(pNode);
            pResponseInfo = NULL;
            pNode = NULL;
            nResult = DoubleLinkedListGetCurrentNode(&pThreadInfo->sChannelRecvResponseBuffer[nChannelNo], &pNode);
            continue;
        }

        // 응답이 전송되지 않았으며 시간초과된 경우
        if (pResponseInfo->bResponse == false && nTimeDiff >= g_nErrorTimeout)// && g_bUseGWTimeout == true)
        {
            WriteLog(LogType_StandardOut, LogLevel_Debug, "Delete Recv Timeouted Message[UUID:%s]", szUUIDStringTemp);
            // 타임아웃 응답 코드 전송 기능 추가
            if (g_bUseGWTimeout)
            {
                SafeMemCopy(aFrameBuffer, sizeof(aFrameBuffer), pResponseInfo->paMessageData, pResponseInfo->nMessageLength);
                SafeMemCopy(aFrameBuffer + SOCK_RESPONSE_CODE_POSITION, SOCK_FRAME_LEN_RESP_CODE, GetSocketRespCode(RESP_TIMEOUT), SOCK_FRAME_LEN_RESP_CODE);
                MakePacket(nChannelNo, aFrameBuffer, pResponseInfo->nMessageLength, aPacketBuffer, &nPacketLength);
                nResult = SafeMemoryAlloc((void *)&pMessageInfo, sizeof(struct MESSAGE_INFO));
                nResult = SafeMemoryAlloc((void *)&pMessageInfo->paMessageData, nPacketLength);
                SafeMemCopy(pMessageInfo->paMessageData, nPacketLength, aPacketBuffer, nPacketLength);
                pMessageInfo->nMessageLength = nPacketLength;
                pNode = NULL;
                nResult = DoubleLinkedListAddNodeToHead(&pThreadInfo->sChannelSendFrameBuffer[nChannelNo], pMessageInfo, &pNode);
            }
            if (!g_bBypassMode)
            {
                amqp_basic_nack(pThreadInfo->sConsumeMQ.sMQConnectionState, 1, pResponseInfo->nDeliveryTag, 0, 1);
            }

            pResponseInfo->bResponse = true;
            pResponseInfo->bTimeout = true;
        }

        if (pResponseInfo->bResponse == true && pResponseInfo->bTimeout == true && pResponseInfo->bResponseRecv == false && nTimeDiff >= g_nTimeoutStoreTerm)
        {
            WriteLog(LogType_StandardOut, LogLevel_Debug, "Delete Recv Dropped Message[UUID:%s]", szUUIDStringTemp);
            nResult = DoubleLinkedListRemoveCurrentNode(&pThreadInfo->sChannelRecvResponseBuffer[nChannelNo], &pNode);
            SafeFree((void **)&pResponseInfo->paMessageData);
            SafeFree((void **)&pResponseInfo);
            SafeFree((void **)&pNode);
            nResult = DoubleLinkedListGetCurrentNode(&pThreadInfo->sChannelRecvResponseBuffer[nChannelNo], &pNode);
            continue;
        }

        nResult = DoubleLinkedListMoveToNext(&pThreadInfo->sChannelRecvResponseBuffer[nChannelNo], &pNode);
    }

}

//int SendPacket(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo, const char *pUUID, const char *aMessageBody, int nMessageBodySize)
int SendPacket(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo, uuid_t *pUUID, const char *aMessageBody, int nMessageBodySize)
{
    int nPacketLength;
    char aPacketBuffer[SOCK_BUFFER_SIZE + 1];
    char szUUIDString[UUID_STRING_SIZE + 1];
    struct MESSAGE_INFO *pMessageInfo;
    struct DOUBLELINKEDLISTNODE *pNode;
    //struct timespec sTimeSpec;
    //time_t sNow;
    int nResult;

    pMessageInfo = NULL;
    pNode = NULL;
    nPacketLength = sizeof(aPacketBuffer);
    nResult = MakePacket(nChannelNo, aMessageBody, nMessageBodySize, aPacketBuffer, &nPacketLength);
    if (nResult != SOCK_ERR_SUCCESS)
    {
        return nResult;
    }
    nResult = SafeMemoryAlloc((void *)&pMessageInfo, sizeof(struct MESSAGE_INFO));
    if (nResult != STR_ERR_SUCCESS)
    {
        return STR_ERR_NO_MEM;
    }

    nResult = SafeMemSet(pMessageInfo, sizeof(struct MESSAGE_INFO), 0, sizeof(struct MESSAGE_INFO));
    if (nResult != SEC_ERR_SUCCESS)
    {
        SafeFree((void **)&pMessageInfo);
        return SOCK_ERR_MAKE_DATA;
    }
    nResult = SafeMemoryAlloc((void *)&pMessageInfo->paMessageData, nPacketLength + 1);
    if (nResult != STR_ERR_SUCCESS)
    {
        SafeFree((void **)&pMessageInfo);
        return STR_ERR_NO_MEM;
    }
    nResult = SafeMemSet(pMessageInfo->paMessageData, nPacketLength + 1, 0, nPacketLength);
    if (nResult != SEC_ERR_SUCCESS)
    {
        SafeFree((void **)&pMessageInfo->paMessageData);
        SafeFree((void **)&pMessageInfo);
        return SOCK_ERR_MAKE_DATA;
    }
    nResult = SafeStrNCopy(pMessageInfo->paMessageData, nPacketLength, aPacketBuffer, nPacketLength);

    if (nResult != SEC_ERR_SUCCESS)
    {
        SafeFree((void **)&pMessageInfo->paMessageData);
        SafeFree((void **)&pMessageInfo);
        return SOCK_ERR_MAKE_DATA;
    }

    pMessageInfo->nMessageLength = nPacketLength;
    pNode = NULL;

    if (pUUID != NULL)
    {
        nResult = SafeMemCopy(&pMessageInfo->aUUID, sizeof(uuid_t), pUUID, sizeof(uuid_t));
        if (nResult != SEC_ERR_SUCCESS)
        {
            SafeFree((void **)&pMessageInfo->paMessageData);
            SafeFree((void **)&pMessageInfo);
            return SOCK_ERR_MAKE_DATA;
        }
#ifdef _WIN32
        uuid_unparse(pUUID, szUUIDString);
#else
        uuid_unparse(*pUUID, szUUIDString);
#endif
        WriteCommLog(LogType_CommOut, LogLevel_Info, pThreadInfo->nSocket, nChannelNo, 'S', "%-*.*s,%s", UUID_STRING_SIZE, UUID_STRING_SIZE, szUUIDString, pMessageInfo->paMessageData);
    }
    else
    {
        WriteCommLog(LogType_CommOut, LogLevel_Info, pThreadInfo->nSocket, nChannelNo, 'S', "%s", pMessageInfo->paMessageData);
    }

    nResult = DoubleLinkedListAddNodeToHead(&pThreadInfo->sChannelSendFrameBuffer[nChannelNo], pMessageInfo, &pNode);
    return SOCK_ERR_SUCCESS;
}

int GetChannelNo(int nChannelType)
{
    int nChannelNo;

    for (nChannelNo = 0; nChannelNo < sizeof(g_nChannelTypeFlags) / sizeof(int); nChannelNo++)
    {
        if ((g_nChannelTypeFlags[nChannelNo] & nChannelType) != 0)
        {
            return nChannelNo;
        }
    }
    return -1;
}

#ifdef _WIN32
int inet_pton(int af, const char *src, void *dst)
{
  struct sockaddr_storage ss;
  int size = sizeof(ss);
  char src_copy[INET6_ADDRSTRLEN+1];

  ZeroMemory(&ss, sizeof(ss));
  /* stupid non-const API */
  strncpy (src_copy, src, INET6_ADDRSTRLEN+1);
  src_copy[INET6_ADDRSTRLEN] = 0;

  if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) {
    switch(af) {
      case AF_INET:
    *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
    return 1;
      case AF_INET6:
    *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
    return 1;
    }
  }
  return 0;
}

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)
{
  struct sockaddr_storage ss;
  unsigned long s = size;

  ZeroMemory(&ss, sizeof(ss));
  ss.ss_family = af;

  switch(af) {
    case AF_INET:
      ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
      break;
    case AF_INET6:
      ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
      break;
    default:
      return NULL;
  }
  /* cannot direclty use &size because of strict aliasing rules */
  return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)?
          dst : NULL;
}
#endif

char *GetSocketRespCode(int nValue)
{
    return GetPairString(g_sRespCodeInfo, sizeof(g_sRespCodeInfo) / sizeof(struct VALUE_STRING_PAIR_INFO), nValue);
}

