套接字

时间:2015-10-21 22:53:53

标签: c++ sockets memory-management memory-leaks memory-leak-detector

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

然后我解开两个盒子,然后就出来了。 enter image description here

任何帮助将不胜感激!

代码:

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

}

2 个答案:

答案 0 :(得分:1)

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

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

在这里,您要通过malloc()HandleStruct分配内存,而free()后续呼叫似乎无法在任何地方进行清理。您将arg传递给handle(),但仍然没有释放内存。

似乎 handle()负责清除arg,因此您应该在free()之后调用while {1}}循环,或者你可以在每个循环开始时分配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();
    }
}

答案 1 :(得分:1)

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

这里是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.

操作系统可能无法清理它们。

如果创建在errno设置为EAGAIN之前很久没有失败,那么您也尝试创建10000个线程,这些线程也可能占用一些内存。