MST

星途 面试题库

面试题:网络编程之libevent基础应用

在基于libevent的游戏服务器网络架构中,简述如何使用libevent库来处理多个并发的TCP连接,并实现简单的消息收发功能,请给出关键代码片段及简要说明。
25.0万 热度难度
后端开发网络编程

知识考点

AI 面试

面试题答案

一键面试

关键代码片段及说明

  1. 初始化libevent库
#include <event2/event.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

// 全局变量,保存event_base
struct event_base *base;

// 初始化libevent库
void init_libevent() {
    base = event_base_new();
    if (!base) {
        fprintf(stderr, "Could not initialize libevent!\n");
        exit(1);
    }
}

此部分代码初始化了libevent库,创建了一个event_base对象,后续的事件都将在这个event_base上进行管理。

  1. 创建监听套接字并设置事件
// 监听回调函数
void listen_cb(evutil_socket_t listen_fd, short events, void *arg) {
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    int client_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_len);
    if (client_fd < 0) {
        perror("accept");
        return;
    }

    // 设置为非阻塞
    evutil_make_socket_nonblocking(client_fd);

    // 创建读事件
    struct event *read_event = event_new(base, client_fd, EV_READ | EV_PERSIST, read_cb, (void *)(intptr_t)client_fd);
    if (!read_event) {
        fprintf(stderr, "Failed to create read event!\n");
        close(client_fd);
        return;
    }

    // 添加读事件到event_base
    event_add(read_event, NULL);
}

// 设置监听
void setup_listen() {
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd < 0) {
        perror("socket");
        exit(1);
    }

    // 设置为非阻塞
    evutil_make_socket_nonblocking(listen_fd);

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind");
        close(listen_fd);
        exit(1);
    }

    if (listen(listen_fd, 10) < 0) {
        perror("listen");
        close(listen_fd);
        exit(1);
    }

    // 创建监听事件
    struct event *listen_event = event_new(base, listen_fd, EV_READ | EV_PERSIST, listen_cb, NULL);
    if (!listen_event) {
        fprintf(stderr, "Failed to create listen event!\n");
        close(listen_fd);
        exit(1);
    }

    // 添加监听事件到event_base
    event_add(listen_event, NULL);
}

此部分代码创建了一个监听套接字,绑定到指定端口并开始监听。当有新连接到来时,listen_cb函数被调用,在该函数中接受新连接,设置为非阻塞,并为新连接创建读事件。

  1. 处理读事件(接收消息)
// 读回调函数
void read_cb(evutil_socket_t client_fd, short events, void *arg) {
    char buffer[1024];
    int n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
    if (n <= 0) {
        if (n == 0) {
            printf("Client disconnected\n");
        } else {
            perror("recv");
        }
        // 移除事件并关闭套接字
        struct event *ev = event_self_cbarg();
        event_free(ev);
        close(client_fd);
        return;
    }

    buffer[n] = '\0';
    printf("Received: %s\n", buffer);

    // 简单回应消息
    const char *response = "Message received!";
    send(client_fd, response, strlen(response), 0);
}

当有数据可读时,read_cb函数被调用,从套接字接收数据,打印接收到的消息,并简单地回显一条确认消息。如果接收数据出错或连接关闭,相应地处理事件并关闭套接字。

  1. 进入事件循环
int main() {
    init_libevent();
    setup_listen();

    // 进入事件循环
    event_base_dispatch(base);

    // 清理
    event_base_free(base);
    return 0;
}

最后,在main函数中初始化libevent,设置监听,然后进入事件循环event_base_dispatch,该函数会阻塞并处理注册的事件,直到所有事件处理完毕或显式退出。程序结束时,释放event_base资源。

通过以上步骤,使用libevent库实现了处理多个并发TCP连接以及简单的消息收发功能。在实际应用中,可能需要更复杂的消息处理逻辑和错误处理机制。