#include "stdafx.h"
#include "TCPClient.h"
#include "CritSection.h"

#define BUF_LEN 8192

CTCPClient::CTCPClient()
{
	Global::Init();
	m_socket = INVALID_SOCKET;
	//m_sin = {0};
	m_port = 0;
}

CTCPClient::~CTCPClient()
{
	DisConnect();
	WSACleanup();
}

// ��ʼ��WinSock 2.2
bool CTCPClient::InitSocket()
{
	WSADATA wsaData;
	return (NO_ERROR == WSAStartup(MAKEWORD(2, 2), &wsaData));
}

bool CTCPClient::Connect(std::string ip, int port)
{
	DisConnect();
	m_ip = ip;
	m_port = port;
	// �����׽���;
	m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_socket == INVALID_SOCKET)
	{
		Global::WriteTextLog(_T("�����׽���ʧ�ܣ�%d"), WSAGetLastError());
		return false;
	}

	// ���ӷ�����;
	m_sin.sin_family = AF_INET;
	m_sin.sin_port = htons(port);
	if (-1 == inet_pton(AF_INET, ip.c_str(), &m_sin.sin_addr))
	{
		Global::WriteTextLog(_T("��ַת��ʧ�ܣ�%d"), WSAGetLastError());
		return false;
	} 

	if (connect(m_socket, (LPSOCKADDR)& m_sin, sizeof(m_sin)) == SOCKET_ERROR)
	{
		Global::WriteTextLog(_T("���ӷ����ʧ�ܣ�%d"), WSAGetLastError());
		closesocket(m_socket);
		return false;
	}

	// �����շ���ʱ;
	int nNetTimeout = 3000; //1��
	//����ʱ��
	setsockopt(m_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)& nNetTimeout, sizeof(int));
	//����ʱ��
	setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)& nNetTimeout, sizeof(int));
	
	// ���û����С;
	int bufSize = BUF_LEN;
	setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char*)& bufSize, sizeof(int));
	setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)& bufSize, sizeof(int));

	return true;
}

bool CTCPClient::SelectConnect(std::string ip, int port, int time_out)
{
	DisConnect();
	m_ip = ip;
	m_port = port;
	// �����շ���ʱ;
	int nNetTimeout = 3000; //1��

	// �����׽���;
	m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_socket == INVALID_SOCKET)
	{
		Global::WriteTextLog(_T("�����׽���ʧ�ܣ�%d"), WSAGetLastError());
		return false;
	}

	// ���ӷ�����;
	m_sin.sin_family = AF_INET;
	m_sin.sin_port = htons(port);
	if (-1 == inet_pton(AF_INET, ip.c_str(), &m_sin.sin_addr))
	{
		Global::WriteTextLog(_T("��ַת��ʧ�ܣ�%d"), WSAGetLastError());
		return false;
	}

	// ��������socketΪ������ģʽ;
	unsigned long um = 1;
	int nRet = ioctlsocket(m_socket, FIONBIO, &um);
	if (nRet != NO_ERROR)
	{
		// ����ʧ��;
		Global::WriteTextLog(_T("���÷�����ģʽʧ�ܣ�%d"), WSAGetLastError());
		return false;
	}

	bool bRet = false;
	nRet = connect(m_socket, (LPSOCKADDR)& m_sin, sizeof(m_sin));
	if (nRet == NO_ERROR)
	{
		bRet = true;
		goto end;
	}

	// ��Ϊ�Ƿ������ģ����ʱ�������Ӧ����WSAEWOULDBLOCK��Linux����EINPROGRESS
	if (nRet < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
	{
		bRet = false;
		goto end;
	}

	TIMEVAL Timeout;
	Timeout.tv_sec = time_out;
	Timeout.tv_usec = 0;

	// ����Ƿ����ӳɹ�;
	fd_set write, err;
	FD_ZERO(&write);
	FD_ZERO(&err);
	FD_SET(m_socket, &write);
	FD_SET(m_socket, &err);

#if 0
	nRet = select(0, NULL, &write, &err, &Timeout);
	if (FD_ISSET(m_socket, &write))
	{
		bRet = true;
	}
#else
	nRet = select(0, NULL, &write, &err, &Timeout);
	if (nRet == 0)
	{
		Global::WriteTextLog(_T("���ӷ���˳�ʱ"));
	}
	else if (nRet < 0)
	{
		Global::WriteTextLog(_T("���ӷ���˳�����%d"), WSAGetLastError());
	}
	else
	{
		int nError = -1;
		int nLen = sizeof(int);
		// ʹ��getsockopt������׽����Ƿ��쳣;
		getsockopt(m_socket, SOL_SOCKET, SO_ERROR, (char*)& nError, &nLen);
		if (nError == 0)
		{
			bRet = true;
		}
		else
		{
			bRet = false;
			Global::WriteTextLog(_T("���ӷ���˳�����%d"), nError);
		}
	}
#endif

end:
	um = 0;
	ioctlsocket(m_socket, FIONBIO, &um);
	if (!bRet)
	{
		Global::WriteTextLog(_T("���ӷ����ʧ�ܣ�%d"), WSAGetLastError());
		closesocket(m_socket);
	}
	else {
		//����ʱ��
		setsockopt(m_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)& nNetTimeout, sizeof(int));
		//����ʱ��
		setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)& nNetTimeout, sizeof(int));
		// ���û����С;
		int bufSize = BUF_LEN;
		setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char*)& bufSize, sizeof(int));
		setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)& bufSize, sizeof(int));
	}

	return bRet;
}

void CTCPClient::DisConnect()
{
	if (m_socket != INVALID_SOCKET)
	{
		shutdown(m_socket, SD_BOTH);
		closesocket(m_socket);
	}
}

bool CTCPClient::Send(std::string s_data, std::string &r_data)
{
	static ThreadSection _critSection;
	AutoThreadSection aSection(&_critSection);

	r_data.clear();
	if (m_socket == INVALID_SOCKET)
	{
		Global::WriteTextLog(_T("SOCKETδ����, �봴�������ӷ�����"));
		return false;
	}

	// ���������������;
	int nRet = send(m_socket, s_data.c_str(), s_data.size(), 0);
	if (nRet < 0 )
	{
		DWORD dwError = WSAGetLastError();
		Global::WriteTextLog(_T("��������ʧ�ܣ�%d"), dwError);
		ReConnect(dwError);
		return false;
	}

	// ���շ��������ص�����;
	char recbuf[64] = { 0 };
	for (int i = 0; i < 10; i++ )
	{
		memset(recbuf, 0, 64);
		nRet = recv(m_socket, recbuf, 64, 0);
		if (nRet < 0)
		{
			DWORD dwError = WSAGetLastError();
			Global::WriteTextLog(_T("��������ʧ�ܣ�%d"), dwError);
			ReConnect(dwError);
			return false;
		}

		// �洢���;
		if ( nRet > 0)		
			r_data.append(recbuf, nRet);

		if ( checkEOM(r_data) || !_tcsicmp(r_data.c_str(), _T("OK\n")) )
			break;
	}

	return true;
}

void CTCPClient::ReConnect(DWORD dwError)
{
	bool bReConnect = false;
	// send/recv error;
	if (dwError == WSAENETRESET)
	{
		bReConnect = true;
		Global::WriteTextLog(_T("���ڱ��ֻ��ڲ������ڽ���ʱ��⵽���ϣ���������ѶϿ���"));
	}
	else if (dwError == WSAECONNABORTED)
	{
		bReConnect = true;
		Global::WriteTextLog(_T("���ڳ�ʱ���������ϣ����·��ֹ��Ӧ�ó���Ӧ�ر��׽��֣���Ϊ�����ٿ��á�"));
	}
	else if (dwError == WSAECONNRESET)
	{
		bReConnect = true;		
		Global::WriteTextLog(_T("�����·��Զ�̶�ִ��Ӳ�رջ���ֹ�ر����á�����UDP�׽��֣�Զ�������޷�������ǰ���͵�UDP���ݱ�����ʹ�á��˿��޷����ʡ�ICMP���ݰ�������Ӧ��Ӧ�ó���Ӧ�ر��׽��֣���Ϊ�����ٿ��á�"));
	}
	else if (dwError == WSAETIMEDOUT)
	{//����������ϻ���һ�˵�ϵͳ��û��֪ͨ������·������ϣ������ѱ�������
		bReConnect = true;
		Global::WriteTextLog(_T("���ڳ�ʱ���������ϣ����·��ֹ��Ӧ�ó���Ӧ�ر��׽��֣���Ϊ�����ٿ��á�"));
	}

	if ( bReConnect )
	{
		DisConnect();
		Connect(m_ip, m_port);
	}
}

bool CTCPClient::checkEOM(std::string& data)
{
	if (data.size() < 5)
		return false;

	if (_tcsicmp("{\n", data.substr(0, 2).c_str()) != 0)
		return false;

	if (_tcsicmp("\n}\n", data.substr(data.size() - 3, 3).c_str()) != 0)
		return false;

	// ������ȥ�������ŵ�����;
	//data = data.substr(2, data.size() - 5);

	return true;
}