【发布时间】:2020-09-20 17:38:12
【问题描述】:
我的 IOCP 服务器程序在运行时会消耗越来越多的内存。在跟踪内存泄漏后,我发现一些 WSAOVERLAPPED 结构提要到 WSARecv() 永远不会被回收。我认为这是因为一些恶意客户端套接字只建立连接但从不发送数据或关闭。所以我在每个客户端套接字上设置了一个 TimerQueueTimer() 来识别超时套接字并删除它们。但是如果我在删除恶意套接字时释放 WSAOVERLAPPED 结构,过了一会儿,我得到“释放后在 014C7DA8 修改的 Free Heap block 014C7D80”。
以下是一些相关代码:
typedef struct _SocketState
{
char operation;
SOCKET socket;
DWORD length;
HANDLE hTimer;
HANDLE hCompletion;
WSAOVERLAPPED* thisOvl;
char buf[MAX_BUF];
} SocketState;
static WSAOVERLAPPED* new_overlapped(void)
{
return (WSAOVERLAPPED*)calloc(1, sizeof(WSAOVERLAPPED));
}
static void create_io_completion_port(void)
{
cpl_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!cpl_port)
{
int err = WSAGetLastError();
exit(1);
}
}
static void post_reading(SocketState* socketState, WSAOVERLAPPED* ovl)
{
DWORD flags = 0;
WSABUF wsabuf = { MAX_BUF, socketState->buf };
int err=0;
memset(ovl, 0, sizeof(WSAOVERLAPPED));
socketState->operation = OP_READ;
socketState->thisOvl=ovl;
if (WSARecv(socketState->socket, &wsabuf, 1, NULL, &flags, ovl, NULL)== SOCKET_ERROR)
{
err = WSAGetLastError();
if (err != WSA_IO_PENDING)
{
printf("[%s:%d]WSARecv error\n", __FUNCTION__, __LINE__);
destroy_connection(socketState, ovl);
return ;
}
}
c_WSARecv++;
}
static void destroy_connection(SocketState* socketState, WSAOVERLAPPED* ovl)
{
int err=0;
if(socketState->hTimer != NULL)
{
DeleteTimerQueueTimer(hTimerQueue,socketState->hTimer,INVALID_HANDLE_VALUE);
socketState->hTimer = NULL;
}
socketState->hCompletion=NULL; //newSocketState->hCompletion = cpl_port
closesocket(socketState->socket);
free(socketState);
if(ovl!=0)
{
free(ovl);
}
}
VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
SocketState* clientSocketState=(SocketState*)lpParam;
if (lpParam != NULL)
{
if(clientSocketState->hCompletion != NULL)
{
PostQueuedCompletionStatus(clientSocketState->hCompletion,-2,(ULONG_PTR)clientSocketState,clientSocketState->thisOvl);
//should last parameter be NULL?
//the "-2" is for identify this timeout io after GetQueuedCompletionStatus()
}
}
}
由于我在服务器程序中使用 C 而不是 C++,因此我将自己置于非常尴尬的境地。基本上我在 C 中找不到一个非常好的 IOCP 示例 :(.
【问题讨论】:
-
所以使用任何异步版本的读取超时来检测流氓客户端。
-
解决方案 - 使用引用计数。您将指针传递给
WSAOVERLAPPED- 添加对它的引用。 I/O 完成 - 调用释放。 -
然后
socketState->operation = OP_READ; socketState->thisOvl=ovl;当然是设计错误。socketState不得持有指向重叠的指针。和状态读取 - 无效(按设计)套接字可以立即进行读取和写入。所以有几个 I/O 请求。需要签证 vesra - 重叠必须有指向socketState的指针。并且参考必须在上面 -
您需要创建继承自
OVERLAPPED的self 类,并在此处持有指向SocketState和operation的指针 -
传统上,one-off socket对象和many-off buffer+ovl是不同的结构体。然后每个缓冲区对象都有自己的重叠块,hEvent 指向自己的缓冲区对象,缓冲区对象带有指向套接字对象的指针。您的数据是 umm... 非最佳:(