C++ Winsock 接受内存 leak/Resource 泄漏
C++ Winsock Accept Memory leak/Resource Leak
我写了一个小的测试 tcp 侦听器。所述侦听器通过端口 28328 侦听并且工作出色,预计每次客户端连接到它时都会发生巨大的 resource/memory 泄漏。
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
SOCKET Socket = INVALID_SOCKET;
bool TestServer()
{
WSADATA wsaData = { 0 };
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
return false;
sockaddr_in addr = { 0 };
Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int Enable = 1;
setsockopt(Socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&Enable, sizeof(int));
addr.sin_family = AF_INET;
addr.sin_port = htons(28328);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(Socket, (sockaddr*)&addr, sizeof(sockaddr)))
return false;
if (listen(Socket, 50))
return false;
return true;
}
void Dolisten()
{
if (TestServer())
{
sockaddr_in addr = { 0 };
SOCKET Client_Socket = 0;
int Lenght = sizeof(addr);
for (;;)
{
Client_Socket = INVALID_SOCKET;
Client_Socket = accept(Socket, (struct sockaddr *)&addr, &Lenght);
if (Client_Socket == INVALID_SOCKET)
continue;
printf("Client Connected %X\n", Client_Socket);
shutdown(Client_Socket, 2);
closesocket(Client_Socket);
}
}
}
int main(int argc, char* argv[])
{
Dolisten();
WSACleanup();
return 0;
}
虽然原来的听众比这大得多,可能还有很多我还没有解决的问题,但到目前为止,这是我最大的问题。
我假设问题是由于接受套接字而导致的,但它没有正确关闭,然后泄漏到句柄泄漏。我基于这样一个事实,即当我查看任务管理器和其他监控进程的工具时,我可以看到句柄数以与我的连接发生相同的速率增加。
注:
1) 看起来泄漏发生在非分页内存上。
2) 如果在 linux 环境中编译和使用相同的代码片段,将不会产生相同的 memory/resource 泄漏。
3) 我已经在多台 windows 机器上编译和测试了这段代码,但出现了同样的问题。
4) (EDIT) 我确实看到一些人在某些 MSDN 论坛和 VS 论坛上发帖时遇到了这个确切的问题,但他们被告知要做的只是提交一张票。
non-paged-pool是一种内核资源,与操作系统无法分页的内存有关,属于稀缺资源。所以关注一下,是件好事。
事实上它在内核中,意味着内存不在您的直接控制之下。可能是内存与未发送、未处理的数据包有关,在这种情况下,资源间接由您的程序负责。
检查句柄泄漏 - 泄漏的来源。 Application Verifier Microsoft : Application Verifier download 可以帮助识别正在泄漏内存和句柄的调用堆栈。
您显示的应用程序中没有内存泄漏。
由于 TCP/IP 的工作方式,无法立即释放与已关闭连接关联的资源。数据包可能会乱序到达或在连接关闭后重新传输。因此,即使在调用 closesocket
之后,实际的 OS 套接字仍会在预定义的时间内保持打开状态(通常为 2-3 分钟,可以使用 TcpTimedWaitDelay 进行调整)。
如果你 运行 netstat -an
,你会看到一堆处于 CLOSE_WAIT 或 TIME_WAIT 状态的连接:
TCP 127.0.0.1:28328 127.0.0.1:56508 TIME_WAIT
TCP 127.0.0.1:28328 127.0.0.1:56510 TIME_WAIT
TCP 127.0.0.1:28328 127.0.0.1:56512 TIME_WAIT
TCP 127.0.0.1:28328 127.0.0.1:56514 TIME_WAIT
TCP 127.0.0.1:28328 127.0.0.1:56516 TIME_WAIT
. . .
当然需要(内核)内存来存储这些临时状态。
此外,临时范围内的 TCP 端口号不能立即重复使用,这意味着您可以 open/close 连接的 速率 非常有限。
我写了一个小的测试 tcp 侦听器。所述侦听器通过端口 28328 侦听并且工作出色,预计每次客户端连接到它时都会发生巨大的 resource/memory 泄漏。
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
SOCKET Socket = INVALID_SOCKET;
bool TestServer()
{
WSADATA wsaData = { 0 };
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
return false;
sockaddr_in addr = { 0 };
Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int Enable = 1;
setsockopt(Socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&Enable, sizeof(int));
addr.sin_family = AF_INET;
addr.sin_port = htons(28328);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(Socket, (sockaddr*)&addr, sizeof(sockaddr)))
return false;
if (listen(Socket, 50))
return false;
return true;
}
void Dolisten()
{
if (TestServer())
{
sockaddr_in addr = { 0 };
SOCKET Client_Socket = 0;
int Lenght = sizeof(addr);
for (;;)
{
Client_Socket = INVALID_SOCKET;
Client_Socket = accept(Socket, (struct sockaddr *)&addr, &Lenght);
if (Client_Socket == INVALID_SOCKET)
continue;
printf("Client Connected %X\n", Client_Socket);
shutdown(Client_Socket, 2);
closesocket(Client_Socket);
}
}
}
int main(int argc, char* argv[])
{
Dolisten();
WSACleanup();
return 0;
}
虽然原来的听众比这大得多,可能还有很多我还没有解决的问题,但到目前为止,这是我最大的问题。
我假设问题是由于接受套接字而导致的,但它没有正确关闭,然后泄漏到句柄泄漏。我基于这样一个事实,即当我查看任务管理器和其他监控进程的工具时,我可以看到句柄数以与我的连接发生相同的速率增加。
注:
1) 看起来泄漏发生在非分页内存上。
2) 如果在 linux 环境中编译和使用相同的代码片段,将不会产生相同的 memory/resource 泄漏。
3) 我已经在多台 windows 机器上编译和测试了这段代码,但出现了同样的问题。
4) (EDIT) 我确实看到一些人在某些 MSDN 论坛和 VS 论坛上发帖时遇到了这个确切的问题,但他们被告知要做的只是提交一张票。
non-paged-pool是一种内核资源,与操作系统无法分页的内存有关,属于稀缺资源。所以关注一下,是件好事。
事实上它在内核中,意味着内存不在您的直接控制之下。可能是内存与未发送、未处理的数据包有关,在这种情况下,资源间接由您的程序负责。
检查句柄泄漏 - 泄漏的来源。 Application Verifier Microsoft : Application Verifier download 可以帮助识别正在泄漏内存和句柄的调用堆栈。
您显示的应用程序中没有内存泄漏。
由于 TCP/IP 的工作方式,无法立即释放与已关闭连接关联的资源。数据包可能会乱序到达或在连接关闭后重新传输。因此,即使在调用 closesocket
之后,实际的 OS 套接字仍会在预定义的时间内保持打开状态(通常为 2-3 分钟,可以使用 TcpTimedWaitDelay 进行调整)。
如果你 运行 netstat -an
,你会看到一堆处于 CLOSE_WAIT 或 TIME_WAIT 状态的连接:
TCP 127.0.0.1:28328 127.0.0.1:56508 TIME_WAIT
TCP 127.0.0.1:28328 127.0.0.1:56510 TIME_WAIT
TCP 127.0.0.1:28328 127.0.0.1:56512 TIME_WAIT
TCP 127.0.0.1:28328 127.0.0.1:56514 TIME_WAIT
TCP 127.0.0.1:28328 127.0.0.1:56516 TIME_WAIT
. . .
当然需要(内核)内存来存储这些临时状态。
此外,临时范围内的 TCP 端口号不能立即重复使用,这意味着您可以 open/close 连接的 速率 非常有限。