套接字内存泄漏

Memory leak on socket

我正在编写一个 tcp 代理,虽然它似乎可以工作,但它留下了内存泄漏。我操纵代码将传入的数据包转发给它自己以创建 10000 个套接字并关闭它们以查看泄漏的位置。但是我无法弄清楚。我用过 deleaker,它没有显示任何泄漏(除了我不关心的小泄漏。)

但后来我取消勾选这两个框,结果就出来了。

如有任何帮助,我们将不胜感激!

代码:

#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <tchar.h>
#include <process.h> /* _beginthread() */

// Need to link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

#define PORT    "1234" /* Port to listen on */
#define BUF_SIZE 4096    /* Buffer for  transfers */

typedef struct {

    char *host;
    char *port;
    SOCKET sock;

}
HandleStruct;

unsigned int S2C(SOCKET from, SOCKET to)
{

    char buf[BUF_SIZE];
    unsigned int disconnected = 0;
    size_t bytes_read, bytes_written;
    bytes_read = recv(from, buf, BUF_SIZE, 0);
    if (bytes_read == 0) {

        disconnected = 1;

    }

    else {

        bytes_written = send(to, buf, bytes_read, 0);
        if (bytes_written == -1) {

            disconnected = 1;

        }


    }

    return disconnected;

}

unsigned int C2S(SOCKET from, SOCKET to)
{

    char buf[BUF_SIZE];
    unsigned int disconnected = 0;
    size_t bytes_read, bytes_written;
    bytes_read = recv(from, buf, BUF_SIZE, 0);
    if (bytes_read == 0) {

        disconnected = 1;

    }

    else {

        bytes_written = send(to, buf, bytes_read, 0);
        if (bytes_written == -1) {

            disconnected = 1;

        }


    }

    return disconnected;

}

void handle(void *param)
{

    HandleStruct *args = (HandleStruct*) param;
    SOCKET client = args->sock;
    const char *host = args->host;
    const char *port = args->port;
    SOCKET server = -1;
    unsigned int disconnected = 0;
    fd_set set;
    unsigned int max_sock;
    struct addrinfo *res = NULL;
    struct addrinfo *ptr = NULL;
    struct addrinfo hints;
    /* Get the address info */
    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    if (getaddrinfo(host, port, &hints, &res) != 0) {

        perror("getaddrinfo");
        closesocket(client);
        return;

    }

    /* Create the socket */
    server = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (server == INVALID_SOCKET) {

        perror("socket");
        closesocket(client);
        return;

    }

    /* Connect to the host */
    if (connect(server, res->ai_addr, res->ai_addrlen) == -1) {

        perror("connect");
        closesocket(client);
        return;

    }

    if (client > server) {

        max_sock = client;

    }

    else {

        max_sock = server;

    }

    /* Main transfer loop */
    while (!disconnected) {

        FD_ZERO(&set);
        FD_SET(client, &set);
        FD_SET(server, &set);
        if (select(max_sock + 1, &set, NULL, NULL, NULL) == SOCKET_ERROR) {

            perror("select");
            break;

        }

        if (FD_ISSET(client, &set)) {

            disconnected = C2S(client, server);

        }

        if (FD_ISSET(server, &set)) {

            disconnected = S2C(server, client);

        }


    }

    closesocket(server);
    closesocket(client);
fprintf(stderr, "Sockets Closed: %d/%d", server, client);
    _endthread();
    return;

}

int _tmain(int argc)
{

    WORD wVersion = MAKEWORD(2, 2);
    WSADATA wsaData;
    int iResult;
    SOCKET sock;
    struct addrinfo hints, *res;
    int reuseaddr = 1; /* True */
    /* Initialise Winsock */
    if (iResult = (WSAStartup(wVersion, &wsaData)) != 0) {

        fprintf(stderr, "WSAStartup failed: %dn", iResult);
        return 1;

    }

    char * host = "127.0.0.1";
    char * port = "1234";

    /* Get the address info */
    ZeroMemory(&hints, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    if (getaddrinfo(NULL, PORT, &hints, &res) != 0) {

        perror("getaddrinfo");
        WSACleanup();
        return 1;

    }

    /* Create the socket */
    sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sock == INVALID_SOCKET) {

        perror("socket");
        WSACleanup();
        return 1;

    }

    /* Enable the socket to reuse the address */
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr,
    sizeof(int)) == SOCKET_ERROR) {

        perror("setsockopt");
        WSACleanup();
        return 1;

    }

    /* Bind to the address */
    if (bind(sock, res->ai_addr, res->ai_addrlen) == SOCKET_ERROR) {

        perror("bind");
        WSACleanup();
        return 1;

    }

    /* Listen */
    if (listen(sock, 6500) == SOCKET_ERROR) {

        perror("listen");
        WSACleanup();
        return 1;

    }

    freeaddrinfo(res);
    int i = 0;

    HandleStruct *arg;
    arg = (HandleStruct *)malloc(sizeof( HandleStruct));
/* Main loop */
    while(1) {

        int size = sizeof(struct sockaddr);
        struct sockaddr_in their_addr;
        SOCKET newsock;
        ZeroMemory(&their_addr, sizeof (struct sockaddr));
        newsock = accept(sock, (struct sockaddr*)&their_addr, &size);

        if (newsock == INVALID_SOCKET) {

            perror("acceptn");

        }
        else {

            arg->sock = newsock;
            arg->host = host;
            arg->port = port;

            if (i < 10000) {
                _beginthread(handle, 0, (void*) arg);
                i++;
            }
        }
    }

    closesocket(sock);
    WSACleanup();
    return 0;

}

我不熟悉阅读您发布的屏幕截图中的程序;但是,您可能应该关注这一行:

arg = (HandleStruct *)malloc(sizeof( HandleStruct));

此处您通过 malloc() which doesn't appear to be cleaned up anywhere with a subsequent call to free()HandleStruct 分配内存。您将 arg 传递给 handle() 但仍然没有释放内存。

似乎没有 handle() 清理 arg 的责任,所以您可能应该打电话给 free()while 循环之后,或者您可以在每个循环的开始分配 HandleStruct 并在结束时释放它。

或者你可以省去麻烦并使用 std::unique_ptr, and optionally change your threads to std::thread,它自己记录谁拥有内存等:

void handle(std::unique_ptr<HandleStruct> args)
{
    // Manipulate args
    ...
}

int main()
{
    std::unique_ptr<HandleStruct> pHandle = std::make_unique<HandleStruct>();
    for (;;)
    {
        ...
        pHandle->sock = newsock;
        pHandle->host = host;
        pHandle->port = port;
        // Create thread:
        std::thread t(&handle, pHandle);
        // Wait for thread to finish so pHandle doesn't change while we are using it on another thread
        // t.join();
    }
}

每个套接字都使用操作系统中的一些内存。

这里是Linux中的描述:accept

   ENOBUFS, ENOMEM
          Not enough free memory.  This often means that the memory
          allocation is limited by the socket buffer limits, not by the
          system memory.

OS 可能无法清理它们。

您还尝试创建 10000 个线程,如果在将 errno 设置为 EAGAIN 后不久创建失败,这些线程也可能会占用一些内存。