#include "TcpSocketClient.h"
#include <mstcpip.h>
#include <WS2tcpip.h>

//自定义错误码
#pragma comment (lib, "ws2_32.lib")  //加载 ws2_32.dll

CTcpSocketClient::CTcpSocketClient()
    :m_isInited(false),m_isConnected(false)
{
}

CTcpSocketClient::~CTcpSocketClient()
{
}

bool CTcpSocketClient::Init()
{
    if (!m_isInited)
    {
        m_isInited = true;
        //初始化socket环境
        WORD wVersionRequested;
        WSADATA wsaData;
        wVersionRequested = MAKEWORD(2, 1);
        int err = WSAStartup(wVersionRequested, &wsaData);
        if (err != 0)
        {   
            Uinit();
            return false;
        }

        m_socketClient = socket(AF_INET, SOCK_STREAM, 0);                    //创建socket
        if (NULL == m_socketClient)
        {
            Uinit();
            return false;
        }

        BOOL bKeepAlive = TRUE;
        int nRet = setsockopt(m_socketClient, SOL_SOCKET, SO_KEEPALIVE,
            (char*)&bKeepAlive, sizeof(bKeepAlive));
        if (nRet == SOCKET_ERROR)
        {
            long lErr = WSAGetLastError();
            Uinit();
            return false;
        }

        ////创建守护线程
    }

    m_isInited = true;
    return true;
}

bool CTcpSocketClient::Connect(const char* ip, int port, int timeoutSecs)
{
    if (!m_isInited)
    {
        return false;
    }
    if (m_isConnected)
    {
        return true;
    }
   
    tcp_keepalive alive_in;
    tcp_keepalive alive_out;
    alive_in.keepalivetime = 5000;
    alive_in.keepaliveinterval = 1000;
    alive_in.onoff = TRUE;
    unsigned long ulBytesReturn = 0;

    int nRet = WSAIoctl(m_socketClient, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in),
        &alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);
    if (nRet == SOCKET_ERROR)
    {
        Uinit();
        return false;
    }

    m_isConnected = tcp_connect_timeout(m_socketClient, ip, port, timeoutSecs);
    return m_isConnected;
}

bool CTcpSocketClient::Disconnect()
{
    if (INVALID_SOCKET != m_socketClient)
    {
        shutdown(m_socketClient, SD_BOTH);
        closesocket(m_socketClient);
        m_socketClient = INVALID_SOCKET;
    }
    m_isConnected = FALSE;
    return true;
}

bool CTcpSocketClient::Uinit()
{
    m_isInited = FALSE;
    Disconnect();
    /*if (m_hReConnSemaphore)
        ReleaseSemaphore(m_hReConnSemaphore, 1, 0);
    if (m_hReConnThread)
        WaitForSingleObject(m_hReConnThread, INFINITE);
    if (m_hReConnSemaphore)
        CloseHandle(m_hReConnSemaphore);
    if (m_hReConnThread)
        CloseHandle(m_hReConnThread);
    m_hReConnSemaphore = NULL;
    m_hReConnThread = NULL;*/
    m_isInited = FALSE;
    WSACleanup();
    return true;
}

bool CTcpSocketClient::SendCommand(const string sendBuff, int timeoutMs, string& recvBuff)
{
    //发送执行指令，并且接收。等待超时时间为 nTimeOutMs毫秒
    //设置发送和接收超时
    setsockopt(m_socketClient, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeoutMs, sizeof(int));
    int nRet = send(m_socketClient, sendBuff.c_str(), sendBuff.length(), 0);
    if (nRet <= 0)
    {
        //这儿可以不考虑缓冲区可能出现的问题，直接重连
        //ReleaseReconnSem();
        /*m_sec.release();
        return MY_RET_SEND_FAILED;*/
        return false;
    }
    else
    {
        //查询才需要接收返回
        char szBuf[512];
        setsockopt(m_socketClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeoutMs, sizeof(int));
        nRet = recv(m_socketClient, szBuf, 100, 0);
        if (nRet <= 0)
        {
            ////这儿可以不考虑缓冲区可能出现的问题，直接重连
            /*ReleaseReconnSem();
            m_sec.release();
            return MY_RET_RECV_FAILED;*/
            return false;
        }
        if (nRet >= 512)
        {
            szBuf[511] = '\0';
        }
        else
        {
            szBuf[nRet] = '\0';
        }
        recvBuff = szBuf;
    }
    //m_sec.release();
    return true;
}

int CTcpSocketClient::Send(const char * buff, int buffLen)
{
    if (!m_isConnected)
        return SOCKET_ERROR;
    
    int nRet = send(m_socketClient, buff, buffLen, 0);
    return nRet;
}

int CTcpSocketClient::Recv(char * buff, int buffLen, int timeoutMs)
{
    if (!m_isConnected)
        return SOCKET_ERROR;

    setsockopt(m_socketClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeoutMs, sizeof(int));
    int nRet = recv(m_socketClient, buff, buffLen, 0);
    if (nRet == 0)
        return 0;

    if (nRet >= buffLen)
    {
        nRet = buffLen;
        buff[buffLen-1] = '\0';
    }
    else
    {
        buff[nRet] = '\0';
    }

    return nRet;
}



static bool tcp_connect_timeout(SOCKET sockfd, string strIP, int nPort, int nSec)
{
    struct sockaddr_in serv_addr;

    //以服务器地址填充结构serv_addr
    serv_addr.sin_family = AF_INET;
    inet_pton(AF_INET, strIP.c_str(), &serv_addr.sin_addr.s_addr);
    serv_addr.sin_port = htons(nPort);
    int error = -1;
    int len = sizeof(int);
    timeval tm;
    fd_set set;
    unsigned long ul = 1;
    ioctlsocket(sockfd, FIONBIO, &ul); //设置为非阻塞模式
    bool ret = false;
    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
    {
        tm.tv_sec = nSec;
        tm.tv_usec = 0;
        FD_ZERO(&set);
        FD_SET(sockfd, &set);
        if (select(sockfd + 1, NULL, &set, NULL, &tm) > 0)
        {
            getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, /*(socklen_t *)*/&len);
            if (error == 0)
                ret = true;
            else
                ret = false;
        }
        else
            ret = false;
    }
    else
        ret = true;


    if (!ret)
    {
        return false;
    }

    ul = 0;
    ioctlsocket(sockfd, FIONBIO, &ul); //设置为阻塞模式
    return true;
}