libevent怎么开发服务器?

使用 libevent 开发服务器可以分为以下几个核心步骤:

1. 基本服务器结构

#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <string.h>
#include <stdlib.h>
struct client_info {
    struct bufferevent *bev;
    // 可以添加其他客户端信息
};
// 读取回调函数
void read_cb(struct bufferevent *bev, void *ctx) {
    struct evbuffer *input = bufferevent_get_input(bev);
    struct evbuffer *output = bufferevent_get_output(bev);
    
    // 将输入数据直接回显(echo服务器)
    evbuffer_add_buffer(output, input);
    
    // 或者处理特定协议
    size_t len = evbuffer_get_length(input);
    if (len > 0) {
        char *data = malloc(len + 1);
        evbuffer_remove(input, data, len);
        data[len] = '\0';
        
        printf("Received: %s", data);
        
        // 处理业务逻辑
        // ...
        
        free(data);
    }
}
// 事件回调函数
void event_cb(struct bufferevent *bev, short events, void *ctx) {
    if (events & BEV_EVENT_ERROR) {
        perror("Error from bufferevent");
    }
    if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
        bufferevent_free(bev);
    }
}
// 接受新连接回调
void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd,
                   struct sockaddr *address, int socklen, void *ctx) {
    struct event_base *base = evconnlistener_get_base(listener);
    
    // 为每个新连接创建bufferevent
    struct bufferevent *bev = bufferevent_socket_new(
        base, fd, BEV_OPT_CLOSE_ON_FREE);
    
    // 设置回调函数
    bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
    bufferevent_enable(bev, EV_READ | EV_WRITE);
    
    printf("New client connected\n");
}

2. 完整的服务器示例

#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define PORT 8888
void read_cb(struct bufferevent *bev, void *ctx) {
    struct evbuffer *input = bufferevent_get_input(bev);
    struct evbuffer *output = bufferevent_get_output(bev);
    size_t len = evbuffer_get_length(input);
    
    if (len > 0) {
        char *data = malloc(len + 1);
        evbuffer_remove(input, data, len);
        data[len] = '\0';
        
        printf("Received %zu bytes: %s", len, data);
        
        // 简单的响应
        char response[1024];
        snprintf(response, sizeof(response), 
                "Server received: %s", data);
        bufferevent_write(bev, response, strlen(response));
        
        free(data);
    }
}
void event_cb(struct bufferevent *bev, short events, void *ctx) {
    if (events & BEV_EVENT_ERROR) {
        printf("Bufferevent error: %s\n", 
               evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
    }
    if (events & BEV_EVENT_EOF) {
        printf("Client disconnected\n");
    }
    if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
        bufferevent_free(bev);
    }
}
void accept_error_cb(struct evconnlistener *listener, void *ctx) {
    struct event_base *base = evconnlistener_get_base(listener);
    int err = EVUTIL_SOCKET_ERROR();
    fprintf(stderr, "Got an error %d (%s) on the listener. "
            "Shutting down.\n", err, evutil_socket_error_to_string(err));
    
    event_base_loopexit(base, NULL);
}
int main() {
    struct event_base *base;
    struct evconnlistener *listener;
    struct sockaddr_in sin;
    
    // 创建event_base
    base = event_base_new();
    if (!base) {
        fprintf(stderr, "Could not initialize libevent!\n");
        return 1;
    }
    
    // 设置服务器地址
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(0);  // 0.0.0.0
    sin.sin_port = htons(PORT);
    
    // 创建监听器
    listener = evconnlistener_new_bind(
        base, accept_conn_cb, NULL,
        LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,
        (struct sockaddr*)&sin, sizeof(sin));
    
    if (!listener) {
        fprintf(stderr, "Could not create a listener!\n");
        return 1;
    }
    
    evconnlistener_set_error_cb(listener, accept_error_cb);
    
    printf("Server started on port %d\n", PORT);
    
    // 进入事件循环
    event_base_dispatch(base);
    
    // 清理资源
    evconnlistener_free(listener);
    event_base_free(base);
    
    return 0;
}

3. 编译命令

编译服务器
gcc -o server server.c -levent
如果使用OpenSSL
gcc -o server server.c -levent -levent_openssl -lssl -lcrypto
调试版本
gcc -g -o server server.c -levent

4. 高级特性使用

定时器示例

#include <event2/event.h>
void timer_cb(evutil_socket_t fd, short event, void *arg) {
    printf("Timer triggered\n");
}
void add_timer(struct event_base *base) {
    struct timeval tv = {1, 0};  // 1秒
    struct event *timer = event_new(base, -1, EV_PERSIST, timer_cb, NULL);
    event_add(timer, &tv);
}

信号处理

#include <event2/event.h>
#include <signal.h>
void signal_cb(evutil_socket_t fd, short event, void *arg) {
    struct event_base *base = arg;
    printf("Received signal %d, shutting down\n", fd);
    event_base_loopbreak(base);
}
void setup_signal_handler(struct event_base *base) {
    struct event *signal_event = evsignal_new(base, SIGINT, signal_cb, base);
    event_add(signal_event, NULL);
}

5. 性能优化建议

1、使用线程池:对于CPU密集型任务

2、缓冲区管理:合理设置缓冲区大小

3、连接管理:实现连接超时和心跳机制

4、资源回收:及时释放不用的连接

6. 常见问题解决

内存泄漏:确保正确释放所有分配的资源

连接数限制:调整系统文件描述符限制

性能瓶颈:使用epollkqueue作为后端

这个框架可以帮助你快速开始使用 libevent 开发高性能的网络服务器,根据具体需求,你可以在此基础上添加协议解析、业务逻辑处理等功能。

文章摘自:https://idc.huochengrm.cn/fwq/17637.html

评论