1

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

但后来我取消了这两个框,这出来了。 在此处输入图像描述

任何帮助,将不胜感激!

代码:

#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;

}
4

2 回答 2

1

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

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

在这里,您正在为HandleStructvia分配内存,malloc()随后调用free(). 你arg进入handle()但仍然没有释放内存。

清理似乎不是的handle()责任arg,因此您可能应该free()在循环之后调用 to while,或者您可以HandleStruct在每个循环的开头分配 并在结束时释放它。

或者您可以省去自己的麻烦和使用std::unique_ptr,并且可以选择将您的线程更改为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();
    }
}
于 2015-10-21T23:35:19.170 回答
1

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

这里是 Linux 中的描述:接受

   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.

操作系统可能不会清理它们。

您还尝试创建 10000 个线程,这些也可能会占用一些内存,如果在errno设置为EAGAIN.

于 2015-10-21T23:47:19.710 回答