/****************************************************************************
** File name    :  FLTTcpSocket.h
** Description  :  define common api for tcp socket
** Create date  :  2018.09.01
** Auther by    :  Liuyx
** Version info :  V1.0.01
** Copyright (C) 2002-2018 xi'an huatek, Inc Co., Ltd
** Update record:  
** DATE         AUTHER   DESC 
** -------------------------------------------------------------------------               
** 2018.09.01   Liuyx    first build
****************************************************************************/
// #include "StdAfx.h"
#include <errno.h>
#include "HTGlobal.h"
#include "HTTcpSocket.h"

static const char *_FILE_ ="HTTcpSocket.cpp" ;

TTcpSocket::TTcpSocket()
{
    m_tcpPort = 0 ;
    m_server_fd = -1;
    m_sockid = -1;
    #ifdef _WIN32
    WSADATA  Data  ;
    WSAStartup(MAKEWORD(2, 2), &Data) ;
    #endif
}

TTcpSocket::~TTcpSocket()
{
    #ifdef _WIN32
    WSACleanup();
    #endif
}

void TTcpSocket::tcpSetServerfd(int fd)
{
    if (fd > 0) m_server_fd = fd;
}
void TTcpSocket::tcpSetSockID(int fd)
{
    if (fd > 0) m_sockid = fd;
}

int  TTcpSocket::tcpGetServerfd()
{
    return m_server_fd;
}
int  TTcpSocket::tcpGetSockID()
{
    return m_sockid;
}
//关闭socket
int TTcpSocket::tcpCloseSocket(int sockfd)
{
    if(sockfd > 0) {
        #ifdef _WIN32
           //if(_close(sockfd) == -1)
           if(shutdown(sockfd, 2) == -1)
        #else
           if(close(sockfd) == -1)
        #endif
//        vPrtLogMsg(LOG_WARNG, RET_FAIL, "_close sockid=%d return:%d:%s",sockfd, errno, strerror(errno));
        return 1;
    }
    return 0 ;
}

void TTcpSocket::SetSocketMode(int sockid, int iMode)
{
    //将SOCKET置为非阻塞的I/O
#ifndef _WIN32
    if (fcntl(sockid, F_SETFL, (fcntl(sockid, F_GETFL) | O_NONBLOCK)) == -1) {
        vPrtLogMsg(LOG_WARNG, errno,"error occured when fcntl,sockid:%d msg:%s.",sockid, strerror(errno));
        return ;
    }
#else
    unsigned long flag = iMode ;  //0:阻塞模式 1:非阻塞方式
    if((ioctlsocket(sockid, FIONBIO, &flag)) < 0) {
        vPrtLogMsg(LOG_WARNG, errno,"error occured when ioctlsocket,sockid:%d msg:%s.",sockid, strerror(errno));
        return  ;
    }
#endif
}

// 根据sockid获取源地址和源端口
int TTcpSocket::getPeerInfo(int sockid, unsigned char *sip, unsigned short *sport)
{
    if(sockid < 0) return -1;
    struct sockaddr_in clientaddr;
    #ifndef _WIN32
    socklen_t clilen =sizeof(struct sockaddr_in); ;
    #else
    int clilen = sizeof(struct sockaddr_in) ;
    #endif    

    if(getpeername(sockid, (struct sockaddr *)&clientaddr, &clilen) < 0) {
        vPrtLogMsg(LOG_ERROR, errno, "getpeername failed, sockid=%d", sockid);
        return -1;
    }
    strcpy((char*)sip, inet_ntoa(clientaddr.sin_addr));
    *sport = ntohs(clientaddr.sin_port);
    return 0;
}
// 根据sockid获取目标地址和目标端口
int TTcpSocket::getLocalSocketInfo(int sockid, unsigned char *sip, unsigned short *sport)
{
    if(sockid < 0) return -1;
    struct sockaddr_in clientaddr;
#ifndef _WIN32
    socklen_t clilen =sizeof(struct sockaddr_in); ;
#else
    int clilen = sizeof(struct sockaddr_in) ;
#endif    
    
    if(getsockname(sockid, (struct sockaddr *)&clientaddr, &clilen) < 0) {
        vPrtLogMsg(LOG_ERROR, errno, "getpeername failed, sockid=%d", sockid);
        return -1;
    }
    strcpy((char*)sip, inet_ntoa(clientaddr.sin_addr));
    *sport = ntohs(clientaddr.sin_port);
    return 0;
}
//创建socket TCP服务。
// return val :	 0  -- successed
//	          :	 <0 -- failed
int TTcpSocket::tcpOpenServer(const char *ip,unsigned short port)
{
    int m_sockfd = -1;

    if(!(port > 1024 && port < 65535) )
	{
        vPrtLogMsg(LOG_ERROR, ErrSockParam, "server port:%d is invalid(1024-65535)", port);
        return ErrSockParam;
    }
	
    m_tcpPort = port;
    setvbuf(stdin,NULL,_IONBF,MAX_SBUFF_TCP);
    setvbuf(stdout, NULL, _IONBF, MAX_SBUFF_TCP);

    //创建SOCKET对象
    m_sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if(m_sockfd == -1)
	{
        vPrtLogMsg(LOG_ERROR, ErrOpenServer, "create socket error,msg:%s.",strerror(errno));
        return ErrOpenServer;
    }

    //将SOCKET置为非阻塞的I/O
    // SetSocketMode(m_sockfd) ;

    struct sockaddr_in fsock;
    int iRet = -1;
    #ifndef _WIN32
    bzero((char *)&fsock, sizeof(struct sockaddr_in)) ;
    #else
    memset(&fsock, 0x00, sizeof(struct sockaddr_in)) ;
    #endif
    fsock.sin_family=AF_INET;
    fsock.sin_port=htons(m_tcpPort);
    fsock.sin_addr.s_addr=htoni(INADDR_ANY);
    if (ip) fsock.sin_addr.s_addr=inet_addr(ip);

    int iFlag = 1;
    #ifndef _WIN32
    if(setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&iFlag, sizeof(iFlag)) != 0)
    #else
    if(setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&iFlag, sizeof(iFlag)) != 0)
    #endif
    {
        vPrtLogMsg(LOG_ERROR, ErrSetSockOpt,"setsockopt of so_reuseaddr error."
            "sockid:%d msg:%s.",m_sockfd, strerror(errno)) ;
        tcpCloseSocket(m_sockfd) ;
        return ErrSetSockOpt ;
    }

    if((iRet = bind(m_sockfd, (struct sockaddr*)&fsock, sizeof(fsock))) == -1)
    {
        vPrtLogMsg(LOG_ERROR, ErrBindFail, "socket bind error,sockid:%d msg:%s.",m_sockfd, strerror(errno)) ;
        tcpCloseSocket(m_sockfd) ;
        return ErrBindFail;
    }
    if((iRet = listen(m_sockfd, LISTENQ)) == -1)
    {
        vPrtLogMsg(LOG_ERROR, ErrListenFail,"socket listen error,sockid:%d msg:%s.",m_sockfd, strerror(errno)) ;
        tcpCloseSocket(m_sockfd) ;
        return ErrListenFail;
    }
	
    // m_tcpConnected = true ;
    vPrtLogMsg(LOG_DEBUG, 0, "Acceptting on %s:%d, waitting request...",ip,m_tcpPort) ;
    return m_sockfd ;
}

//侦听是否有连接请求
int TTcpSocket::tcpAcceptSocket(int sockid)
{
    fd_set fd_Read ;
    struct timeval st_TimeOut ;
    int isel = 0 ;
    int iClientID = -1 ;
    struct sockaddr_in cliaddr;

    #ifndef _WIN32
    socklen_t cliAddrLen = sizeof(sockaddr_in) ;
    #else
    int cliAddrLen = sizeof(sockaddr_in) ;
    #endif

    st_TimeOut.tv_sec = 5 ;
    st_TimeOut.tv_usec = 0;
    FD_ZERO(&fd_Read);
    FD_SET((unsigned int)sockid, &fd_Read);
    isel = select(sockid + 1 , &fd_Read, NULL, NULL, &st_TimeOut);
    if(isel == 0 || isel == EINTR) {
        // vPrtLogMsg(LOG_DEBUG, errno, "accept...isel=%d errno=%d",isel, errno);
        return -1 ; 
    }
    else if( FD_ISSET(sockid , &fd_Read) )
    {
        iClientID = accept(sockid, (struct sockaddr*)&cliaddr, &cliAddrLen);
       	if(iClientID > 0)
		{
           	vPrtLogMsg(LOG_DEBUG, 0,"Have once new link from %s:%d sockid:%d.", 
				inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port), iClientID) ;
        }
        else {
            vPrtLogMsg(LOG_DEBUG, errno, "accept...isel=%d errno=%d",isel, errno); 
            return -1;
        }
    }
    SetSocketMode(iClientID); //设置为非阻塞
    int iFlag = 1;
    #ifndef _WIN32
    if(setsockopt(iClientID, SOL_SOCKET, SO_REUSEADDR, (const void *)&iFlag, sizeof(iFlag)) != 0)
    #else
    if(setsockopt(iClientID, SOL_SOCKET, SO_REUSEADDR, (const char *)&iFlag, sizeof(iFlag)) != 0)
    #endif
    {
        vPrtLogMsg(LOG_ERROR, ErrSetSockOpt,"setsockopt of so_reuseaddr error,sockid:%d msg:%s.",iClientID, strerror(errno)) ;
        tcpCloseSocket(iClientID) ;
        return ErrSetSockOpt ;
    }
    tcpSetlinger(iClientID);
    return iClientID ;
}

/************************************************************
** Description :  send data for TCP/IP
** Para in     :  tcpID	   TCP/IP socket description
**		          *buf	   send data buffer
**		          length   send data size
** Para out    :  None
** Return Code :  >0	    send data length
**		          <0	    error
************************************************************/
int  TTcpSocket::tcpSendBuffer(int sockid, const char *pBuffer, int length)
{
	if ((sockid <= 0) || (pBuffer == NULL) )
		return -1 ;
	if(length <= 0) length = strlen(pBuffer);

	int iSendLen = 0,iLen=0,i=0,iCnt=0,len=length ;
    char *p = (char *)pBuffer;
	fd_set fd_Write, fd_Except ;
	struct timeval st_TimeOut ;

	if(length > MAX_SBUFF_TCP)
		iCnt = length/MAX_SBUFF_TCP ;
	else
		iCnt = 0 ;
	for(;;)
	{
		FD_ZERO(&fd_Write) ;
		FD_ZERO(&fd_Except) ;
		FD_SET((unsigned int)sockid, &fd_Write) ;
		FD_SET((unsigned int)sockid, &fd_Except) ;
		iSendLen = -1 ;
		st_TimeOut.tv_sec = 0 ;
		st_TimeOut.tv_usec = 10 ;

		iSendLen = select (sockid+1 , NULL, &fd_Write, &fd_Except, &st_TimeOut) ;
        if((iSendLen == 0 && errno == EPIPE) || (iSendLen < 0)) {  // EPIPE =32  Broken pipe EBADF=9 Bad file number
            vPrtLogMsg(LOG_ERROR,errno, "socket execption close,sockid:%d ret:%d msg:%s", sockid, iSendLen, strerror(errno));
            //add_monitor_mesg(len, 0x00, (unsigned char*)pBuffer); // 添加监控消息到监控发送队列
            return ErrException;
        }
        if( FD_ISSET(sockid,  &fd_Except) )
		{
		    vPrtLogMsg(LOG_ERROR,errno, "socket execption,sockid:%d msg:%s", sockid, strerror(errno));
			//add_monitor_mesg(len, 0x00, (unsigned char*)pBuffer); // 添加监控消息到监控发送队列
            return ErrSendFail ;  //socket exception
		}
        else if( FD_ISSET(sockid , &fd_Write) )
		{
	        iSendLen = 0 ;
			for(i=0; i< iCnt; i++)
			{
				iLen = send(sockid, p+(i*MAX_SBUFF_TCP), MAX_SBUFF_TCP,0);
				if( iLen < 0 )
				{
                    vPrtLogMsg(LOG_ERROR,errno, "send buffer fail,sockid:%d toplen:%ld sendlen:%d msg:%s", 
		                 sockid, len, iSendLen, strerror(errno));
                    if(errno == 0 && iLen == -1) {
                        //add_monitor_mesg(len, 0x00, (unsigned char*)pBuffer); // 添加监控消息到监控发送队列
                        return ErrException;
                    }
                    if(errno == EPIPE || errno == EBADF || errno == ECONNRESET || errno == EINVAL) {
                        //add_monitor_mesg(len, 0x00, (unsigned char*)pBuffer); // 添加监控消息到监控发送队列
                        return ErrException;
                    }
                    else {
                        //add_monitor_mesg(len, 0x00, (unsigned char*)pBuffer); // 添加监控消息到监控发送队列
                        return ErrSendFail ;   // send data fail
                    }
		        }
				length -= MAX_SBUFF_TCP;
				iSendLen += iLen ;
			}
            if(length == 0 ) {
                //add_monitor_mesg(len, 0x01, (unsigned char*)pBuffer); // 添加监控消息到监控发送队列
                return iSendLen ;
            }
			if(length >0)
			{
				iLen = send(sockid, p+(i*MAX_SBUFF_TCP), length,0);
				if(iLen < 0)
				{
                    vPrtLogMsg(LOG_ERROR, errno, "send buffer fail! sockid:%d toplen:%ld sendlen:%d msg:%s", 
		                 sockid, len, iSendLen, strerror(errno));
                    if(errno == 0 && iLen == -1) {
                        //add_monitor_mesg(len, 0x00, (unsigned char*)pBuffer); // 添加监控消息到监控发送队列
                        return ErrException;
                    }
                    if(errno == EPIPE || errno == EBADF || errno == ECONNRESET || errno == EINVAL) {
                        //add_monitor_mesg(len, 0x00, (unsigned char*)pBuffer); // 添加监控消息到监控发送队列
                        return ErrException;
                    }
                    else {
                        //add_monitor_mesg(len, 0x00, (unsigned char*)pBuffer); // 添加监控消息到监控发送队列
                        return ErrSendFail ;   // send data fail
                    }
                }
				break ;
			}
		}
	} //end for(;;)
    //add_monitor_mesg(iSendLen+iLen, 0x01, (unsigned char*)pBuffer); // 添加监控消息到监控发送队列
	return (iSendLen+iLen);
}

/************************************************************
** Description :  recv data for TCP/IP
** Para in     :  tcpID	   TCP/IP socket description
**		          *buf	   send data buffer
**		          length   send data size
** Para out    :  None
** Return Code :  >0	    recv data length
**		          <0	    error
************************************************************/
int TTcpSocket::tcpRecvBuffer(int sockid, char* pBuffer,int nMaxLength, int timeout)
{
	int nLength = -2;
	int iTop = 0;
	fd_set fd_Read,fd_Except;
	struct timeval st_TimeOut;
	int isel = 0;

    if( sockid <= 0 || pBuffer == NULL) return -1;
    while(iTop != nMaxLength) 
    {
        st_TimeOut.tv_sec = timeout ;
        st_TimeOut.tv_usec = 0;
    	FD_ZERO(&fd_Read);
        FD_ZERO(&fd_Except) ;
    	FD_SET((unsigned int)sockid, &fd_Read) ;
        FD_SET((unsigned int)sockid, &fd_Except) ;
    	isel= select (sockid + 1 , &fd_Read, NULL, &fd_Except, &st_TimeOut) ;

        if(isel <= 0 || isel == EINTR)
    	{
            // vPrtLogMsg(LOG_DEBUG,errno, "select socket timeout,isel=%d",isel);
    	    break ;
    	}
        else if( FD_ISSET(sockid,  &fd_Except) )
        {
            vPrtLogMsg(LOG_ERROR,errno,"socket execption,sockid:%d errno:%d iselect:%d", sockid, errno, isel) ;
            return ErrRecvFail;   // recv fail
        }
    	else if (isel > 0)	 //EINTR
    	{
           	if (FD_ISSET(sockid,&fd_Read))      //检测Socket读状态
           	{
           	    nLength = recv(sockid, pBuffer, MAX_SBUFF_TCP, MSG_PEEK); // MSG_PEEK
           	    if ( nLength < 0 && (errno == EPIPE || errno == EBADF || errno == ECONNRESET || errno == EINVAL)) 
                    return ErrException;
                else if( isel == 1 && nLength == 0 && (errno == EPIPE || errno == EBADF || errno == ECONNRESET || errno == EINVAL)) {
                    vPrtLogMsg(LOG_DEBUG,errno, "select socket timeout,isel=%d nLength=%d",isel,nLength);
                    return ErrException; // iTop;  //检查到客户端主动断开连接.
                }
                else if(nLength < 0 && errno == 0) 
                    return ErrException ;
                else if (nLength < 0)
                    return ErrRecvFail;   // recv fail
                else if (nLength == 0)
                    return ErrException; 
                else {
                    iTop += nLength; 
                }
            }
            break ;
    	}
    }
    return iTop;
}

//连接是否已经建立
bool TTcpSocket::tcpIsConnected(int sockid)
{
    char msg[5] = {0};  
    int err = 0;
    if(sockid <= 0 ) return false ;
    err = recv(sockid, msg, sizeof(msg), MSG_PEEK);
    /* client already close connection. */
    if(err < 0 && (errno == EPIPE || errno == EBADF || errno == ECONNRESET))  
          return false;
    return true;  
}

int TTcpSocket::tcpSetlinger(int sockid) 
{ 
    struct linger lingerStruct={1,0}; 

    if(setsockopt(sockid, SOL_SOCKET, SO_LINGER, (char *)&lingerStruct, sizeof(lingerStruct)) == -1) 
        return   -1; 
    return  0; 
}
int TTcpSocket::tcpSetNodelay(int sockid) 
{ 
    int nodelay = 1; 
#ifdef TCP_NODELAY
    if(setsockopt(sockid, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay)) == -1) 
        return   -1; 
#endif
    return  0; 
}

// 连接TCP 服务器
int TTcpSocket::tcpConnect(const char *pszIP, short port, int iTimeout)
{
    int iRet = -1, sockid=-1;
    struct sockaddr_in addr; 
    int on = 1;
	
    if(!pszIP ||  !((unsigned int)port > 1024 && (unsigned int)port < 65535)) {
        vPrtLogMsg(LOG_ERROR, ErrSockParam, "in parameter ip/port %s:%d(1024-65535) invalid.", pszIP, port);
        return ErrSockParam;
    }
    // Create socket
    sockid = socket(AF_INET, SOCK_STREAM, 0);
    if(sockid < 0 )
	{
        vPrtLogMsg (LOG_ERROR, errno, "create socket failed! errno[%d],msg:%s", errno,strerror(errno));
        sockid = -1;
        return sockid; // break;
    }
    
    memset(&addr, 0x00, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(pszIP);  //htoni(INADDR_ANY); 
    
    /* set socket options */
    #ifdef _WIN32
    //setsockopt(sockid, SOL_SOCKET, SO_ERROR, (const char*)&on, sizeof(int));
    if(setsockopt(sockid, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(int)) < 0) {
    #else
    if(setsockopt(sockid, SOL_SOCKET, SO_REUSEADDR, (const void*)&on, sizeof(int)) < 0) {
    #endif
        vPrtLogMsg (LOG_ERROR, errno, "setsockopt error! errno[%d],msg:%s", errno,strerror(errno));
        tcpCloseSocket(sockid);
        sockid = -1;
        return sockid; // break;
    }
    //SetSocketMode(sockid);   // 将建立的新连接置为非阻塞
    iRet = connect(sockid, (struct sockaddr *)&addr, sizeof(addr));
    if (iRet == 0)   {
        tcpSetlinger(sockid);    // 建链成功
        SetSocketMode(sockid);   // 将建立的新连接置为非阻塞
        return sockid;
    }
    tcpCloseSocket(sockid);
    sockid = -1;

    return sockid;
}

void TTcpSocket::clear_tcp_buffer(int socket_fd, int nbytes)
{
    char buffer[MAX_TEMP_BUFFER_SIZE]={0};
	int recv_len;
	int i;
	if(nbytes == 0)  return ;   // 如果需要清除的字节数为0 则直接返回成功
	for(i = 0; i < (int)(nbytes / MAX_TEMP_BUFFER_SIZE); i++) {
		recv_len = recv(socket_fd, buffer, MAX_TEMP_BUFFER_SIZE, 0); 
        if(recv_len  >= 0 && recv_len < MAX_TEMP_BUFFER_SIZE) {       // 期间如果发现缓冲区的数据已经被清空了则返回
			return ;
		}
        else if(recv_len < 0) { // 异常了
			return ;
		}
	}
	recv_len = recv(socket_fd, buffer, (nbytes % MAX_TEMP_BUFFER_SIZE), 0); 
	if(recv_len >= 0) return ;
	else return ;
}
