下面是WSAAsyncSelect的處理代碼,但是總感覺別扭。
1.如果有無聊的人連接上來不發送資料也不關閉連接就永遠不會有FD_CLOSE訊息,導致存盤lp_client_read_info的資訊永遠會保存在list里面。
2.用list保存組包緩沖區,這種方式合不合適呢,一般WSAAsyncSelect模式怎么保存SOCKET組包緩沖區的?
void DSocketServer::OnMessage(SOCKET sock, LPARAM lParam, HWND hWnd)
{
if (WSAGETSELECTERROR(lParam)) {
closesocket(sock);
return;
}
switch (WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT:
{
sockaddr_in clientAddr;
int addr_size = sizeof(sockaddr);
SOCKET clientSock = accept(m_socket_server, (sockaddr*)&clientAddr, &addr_size);
if (INVALID_SOCKET == clientSock)
{
globalUserLog.Error(TEXT("客戶端套按字無效!錯誤碼:%ld"), WSAGetLastError());
return;
}
char *clientIp = inet_ntoa(clientAddr.sin_addr);
// 讀取資料的結構體,第一次進來先需要讀取頭資訊,描述資料包的大小之類的資訊
lp_client_read_info lpReadInfo = new client_read_info{0};
// 保存客戶端的IP
_tcscpy_s(lpReadInfo->client_ip, 88, clientIp);
lpReadInfo->client_sock = clientSock;
// 應該接收的資料長度,h_info頭資訊的結構
lpReadInfo->data_len = sizeof(h_info);
// 接收資料的緩沖
lpReadInfo->data = new TCHAR[lpReadInfo->data_len]{0};
// 將結構體保存到一個list,它是一個類屬性
push_client_info(lpReadInfo);
// 系結READ和CLOSE訊息
WSAAsyncSelect(clientSock, hWnd, WM_ASYNC_SOCKET, FD_READ | FD_CLOSE);
break;
}
case FD_READ:
{
WSAAsyncSelect(sock, hWnd, 0, 0);
// 根據SOCKET取出對應的lp_client_read_info結構
lp_client_read_info lpReadInfo = get_client_info(sock);
if (sock != lpReadInfo->client_sock)
{
return;
}
// 計算剩下需要讀取的資料
UINT readBufLen = lpReadInfo->data_len - lpReadInfo->readed_len;
// 最多一次讀取MAX_READ_BUF個位元組
if (readBufLen > MAX_READ_BUF)
{
readBufLen = MAX_READ_BUF;
}
TCHAR tmpBuf[MAX_READ_BUF] = {0};
int recvLen = recv(sock, tmpBuf, readBufLen, 0);
if (SOCKET_ERROR == recvLen)
{
int wsaErrorNo = WSAGetLastError();
if (wsaErrorNo != WSAEWOULDBLOCK && wsaErrorNo != WSAEINTR)
{
switch (wsaErrorNo)
{
case WSAETIMEDOUT:
{
globalUserLog.Error(TEXT("讀取資料超時,IP:%s"), lpReadInfo->client_ip);
}
break;
case WSAECONNRESET:
{
globalUserLog.Error(TEXT("遠程主機強制斷開連接,IP:%s"), lpReadInfo->client_ip);
}
break;
default:
{
globalUserLog.Error(TEXT("讀取資料時發生未知網路錯誤,錯誤碼:%d,IP:%s"), wsaErrorNo, lpReadInfo->client_ip);
}
break;
}
WSAAsyncSelect(sock, hWnd, WM_ASYNC_SOCKET, FD_CLOSE);
}
else
{
WSAAsyncSelect(sock, hWnd, WM_ASYNC_SOCKET, FD_READ);
}
}
else if (recvLen > 0 && recvLen + lpReadInfo->readed_len > lpReadInfo->data_len)
{
globalUserLog.Error(TEXT("資料可能被篡改,IP:%s"), lpReadInfo->client_ip);
WSAAsyncSelect(sock, hWnd, WM_ASYNC_SOCKET, FD_CLOSE);
}
else
{
// 讀取到資料后保存到緩沖區
if (recvLen > 0)
{
memcpy_s(lpReadInfo->data + lpReadInfo->readed_len, lpReadInfo->data_len - lpReadInfo->readed_len, tmpBuf, recvLen);
lpReadInfo->readed_len += recvLen;
}
// 如果資料讀取完成開始處理資料
if (lpReadInfo->data_len == lpReadInfo->readed_len)
{
// 判斷組包后的資料是不是頭資料包,如果是頭資料包,再繼續讀實際要用的資料
if (!lpReadInfo->readed_header)
{
h_info header_info = {0};
memcpy_s(&header_info, sizeof(h_info), lpReadInfo->data, sizeof(h_info));
delete[] lpReadInfo->data;
lpReadInfo->data = nullptr;
// 標識為頭資料包已經讀完了
lpReadInfo->readed_header = TRUE;
lpReadInfo->data_len = ntohl(header_info.data_len);
lpReadInfo->origin_data_len = ntohl(header_info.origin_data_len);
lpReadInfo->readed_len = 0;
try
{
lpReadInfo->data = new TCHAR[lpReadInfo->data_len]{ 0 };
if (NULL == lpReadInfo->data)
{
WSAAsyncSelect(sock, hWnd, WM_ASYNC_SOCKET, FD_CLOSE);
globalUserLog.Error(TEXT("記憶體分配失敗,分配大小:%ld"), lpReadInfo->data_len);
}
else
{
WSAAsyncSelect(sock, hWnd, WM_ASYNC_SOCKET, FD_READ);
}
}
catch (const std::bad_alloc &memEx)
{
WSAAsyncSelect(sock, hWnd, WM_ASYNC_SOCKET, FD_CLOSE);
globalUserLog.Error(TEXT("記憶體分配發生例外,分配大小:%ld"), lpReadInfo->data_len);
}
}
else
{
// 最后處理資料
EnterCriticalSection(&m_cs);
UINT ret = m_dataHandler.Handler(lpReadInfo->data,lpReadInfo->data_len, lpReadInfo->origin_data_len);
LeaveCriticalSection(&m_cs);
ret = htonl(ret);
int sendRet = send(sock, (const char*)&ret, sizeof(UINT), 0);
if (SOCKET_ERROR == sendRet)
{
globalUserLog.Error(TEXT("回傳結果時發生未知網路錯誤,錯誤碼:%d,IP:%s"), WSAGetLastError(), lpReadInfo->client_ip);
}
WSAAsyncSelect(sock, hWnd, WM_ASYNC_SOCKET, FD_CLOSE);
}
}
else
{
WSAAsyncSelect(sock, hWnd, WM_ASYNC_SOCKET, FD_READ);
}
}
}
break;
case FD_CLOSE:
{
WSAAsyncSelect(sock, hWnd, 0, 0);
// 移除sock對應的資料結構體
remove_client_info(sock);
closesocket(sock);
}
break;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/48103.html
標籤:網絡編程
