这个c ++代码有内存泄漏吗?

时间:2014-06-27 15:18:36

标签: c++ libevent

我试图理解我从this page获得的Libevent c ++代码。
我有点困惑 - 我认为这段代码可能有内存泄漏我是否正确?

似乎ConnectionData指针是在on_connect()回调中创建的,但delete()仅在错误读取或写入完成后调用。 如果连接是accept(),那会怎么样 - 但是没有读取或写入?所以指针只停留在守护进程内存中吗?

#include <event.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <iostream>

// Read/write buffer max length
static const size_t MAX_BUF = 512;

typedef struct {
    struct event ev;
    char         buf[MAX_BUF];
    size_t       offset;
    size_t       size;
} ConnectionData;

void on_connect(int fd, short event, void *arg);
void client_read(int fd, short event, void *arg);
void client_write(int fd, short event, void *arg);

int main(int argc, char **argv)
{
    // Check arguments
    if (argc < 3) {
        std::cout << "Run with options: <ip address> <port>" << std::endl;
        return 1;
    }
    // Create server socket
    int server_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (server_sock == -1) {
        std::cerr << "Failed to create socket" << std::endl;
        return 1;
    }

    sockaddr_in sa;
    int         on      = 1;
    char      * ip_addr = argv[1];
    short       port    = atoi(argv[2]);

    sa.sin_family       = AF_INET;
    sa.sin_port         = htons(port);
    sa.sin_addr.s_addr  = inet_addr(ip_addr);

    // Set option SO_REUSEADDR to reuse same host:port in a short time
    if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
        std::cerr << "Failed to set option SO_REUSEADDR" << std::endl;
        return 1;
    }

    // Bind server socket to ip:port
    if (bind(server_sock, reinterpret_cast<const sockaddr*>(&sa), sizeof(sa)) == -1) {
        std::cerr << "Failed to bind server socket" << std::endl;
        return 1;
    }

    // Make server to listen
    if (listen(server_sock, 10) == -1) {
        std::cerr << "Failed to make server listen" << std::endl;
        return 1;
    }

    // Init events
    struct event evserver_sock;
    // Initialize
    event_init();
    // Set connection callback (on_connect()) to read event on server socket
    event_set(&evserver_sock, server_sock, EV_READ, on_connect, &evserver_sock);
    // Add server event without timeout
    event_add(&evserver_sock, NULL);

    // Dispatch events
    event_dispatch();

    return 0;
}

// Handle new connection {{{
void on_connect(int fd, short event, void *arg) 
{
    sockaddr_in client_addr;
    socklen_t   len = 0;

    // Accept incoming connection
    int sock = accept(fd, reinterpret_cast<sockaddr*>(&client_addr), &len);
    if (sock < 1) { 
        return; 
    }

    // Set read callback to client socket
    ConnectionData * data = new ConnectionData;
    event_set(&data->ev, sock, EV_READ, client_read, data);
    // Reschedule server event
    event_add(reinterpret_cast<struct event*>(arg), NULL);
    // Schedule client event
    event_add(&data->ev, NULL);
}
//}}}

// Handle client request {{{
void client_read(int fd, short event, void *arg)
{
    ConnectionData * data = reinterpret_cast<ConnectionData*>(arg);
    if (!data) {
        close(fd);
        return;
    }
    int len = read(fd, data->buf, MAX_BUF - 1);
    if (len < 1) {
        close(fd);
        delete data;
        return;
    }
    data->buf[len] = 0;
    data->size     = len;
    data->offset   = 0;
    // Set write callback to client socket
    event_set(&data->ev, fd, EV_WRITE, client_write, data);
    // Schedule client event
    event_add(&data->ev, NULL);
}
//}}}

// Handle client responce {{{
void client_write(int fd, short event, void *arg)
{
    ConnectionData * data = reinterpret_cast<ConnectionData*>(arg);
    if (!data) {
        close(fd);
        return;
    }
    // Send data to client
    int len = write(fd, data->buf + data->offset, data->size - data->offset);
    if (len < data->size - data->offset) {
        // Failed to send rest data, need to reschedule
        data->offset += len;
        event_set(&data->ev, fd, EV_WRITE, client_write, data);
        // Schedule client event
        event_add(&data->ev, NULL);
    }
    close(fd);
    delete data;
}
//}}}

1 个答案:

答案 0 :(得分:1)

The documentation for event_set表示唯一有效的事件类型为EV_READEV_WRITE,但回调将使用EV_TIMEOUTEV_SIGNAL,{{1}进行调用},或EV_READ。文档不清楚,但我希望在客户端关闭套接字时调用read回调。我希望EV_WRITE中失败分支中的delete可以处理这种情况。

请注意,仅当客户端发送client_readFIN数据包时才会出现这种情况。客户端可以建立连接并永久保持打开状态。出于这个原因,应修改此代码以使其超时(可能通过event_once)并要求客户端在该超时内发送消息。