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 连接的 速率 非常有限。