WSAAsyncSelect模型接收数组包的问题

qq_40487922 2020-09-15 12:07:18
下面是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;
}
}
...全文
140 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
an_bachelor 2020-09-16
  • 打赏
  • 举报
回复
引用 2 楼 qq_40487922 的回复:
[quote=引用 1 楼 陈仲甫 的回复:]可以在你的业务协议中规定一个失活时间,多久没活动自动RST TCP协议层面也可以通过不打开keepalive选项等它超时自己断开 但逻辑上应该做在业务协议里面 因为这是你业务规则的一部分
感谢。。因为我是第一次写异步网络。 我现在用的方式用list保存组包信息,READ的时候再根据SOCKET从list中取出组包信息继续读取。正常是用这种方式来作的吗?[/quote]其实这个API我没怎么直接用过 因为CAsyncSocket为它提供了更简单的接口 而需要高并发的场景它又不够用 组包的问题 没有收完整的临时数据可以暂存任何地方 只需考虑到方便高效地根据包ID读写这个包的数据(和相关属性 比如接收时间) 跟存储使用的容器/数据结构没有特别的联系
qq_40487922 2020-09-15
  • 打赏
  • 举报
回复
引用 1 楼 陈仲甫 的回复:
可以在你的业务协议中规定一个失活时间,多久没活动自动RST TCP协议层面也可以通过不打开keepalive选项等它超时自己断开 但逻辑上应该做在业务协议里面 因为这是你业务规则的一部分
感谢。。因为我是第一次写异步网络。 我现在用的方式用list保存组包信息,READ的时候再根据SOCKET从list中取出组包信息继续读取。正常是用这种方式来作的吗?
an_bachelor 2020-09-15
  • 打赏
  • 举报
回复
可以在你的业务协议中规定一个失活时间,多久没活动自动RST
TCP协议层面也可以通过不打开keepalive选项等它超时自己断开 但逻辑上应该做在业务协议里面 因为这是你业务规则的一部分

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧