基于libevent写一个服务器(附带源码)

使用libevent搭建服务器

使用开源框架,目的是减少程序员对一些精细的操作的误操作,也是为了让程序员能更好的对接业务而不是底层api的使用。
为何使用libevent,因为libevent开源已经有十几年了,能很好的承受数万的客户端访问,他能活下来和有人维护就是市场对它的认可,而且开源的项目能兼容更多的平台,这为二次开发提供方便。

服务器源码

c 复制代码
#include <iostream>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
#include <event2/event.h>
using namespace std;

#define MAX 1000  // 最大连接数限制

// 结构体定义(拼写错误,应为eventfd)
struct evenfd {
    evutil_socket_t fd;     // 文件描述符
    struct event *ev;       // 对应的事件对象指针
} event[MAX];               // 全局事件管理数组(线程不安全)

/* 初始化全局事件数组 */
void init_ev_fd() {
    int i = 0;
    for (i = 0; i < MAX; i++) {
        event[i].fd = -1;       // 无效文件描述符标记
        event[i].ev = nullptr; // 空事件指针
    }
}

/* 将新连接的事件信息存入全局数组 */
void setEventFd(evutil_socket_t fd, struct event *ev) {
    int i = 0;
    // 查找可用槽位(线性搜索,时间复杂度O(n))
    for (i = 0; i < MAX; i++) {
        if (event[i].fd == -1) break;
    }
    if (i == MAX) {  // 数组已满时直接退出(需改进)
        exit(1);     // 应改为返回错误码或动态扩容
    }
    event[i].fd = fd; // 存储文件描述符
    event[i].ev = ev; // 存储事件指针
}

/* 根据文件描述符查找数组索引 */
int findEv(int fd) {
    int i = 0;
    for (i = 0; i < MAX; i++) {
        if (event[i].fd == fd) break;
    }
    if (i == MAX) {  // 未找到时直接退出(需改进)
        cout << "not find fd" << endl;
        exit(1);     // 应返回-1由上层处理
    }
    return i;
}

/* 读回调函数:处理客户端数据 */
void readcb(evutil_socket_t cfd, short events, void *arg) {
    char buf[1024];
    memset(buf, 0, sizeof(buf));
    int num = findEv(cfd);  // 查找事件索引
    
    int n = read(cfd, buf, sizeof(buf));
    if (n <= 0) {  // 客户端断开或读错误
        cout << "Client closed, n=[" << n << "]" << endl;
        close(cfd);              // 关闭socket
        event_del(event[num].ev); // 移除事件监控
        event_free(event[num].ev); // 释放事件资源
        event[num].fd = -1;      // 重置数组项
        event[num].ev = nullptr;
        return;
    }
    
    // 处理数据:转大写
    for (int i = 0; i < n; i++) {
        buf[i] = toupper(buf[i]);
    }
    write(cfd, buf, n);  // 回写数据(需处理EAGAIN错误)
}

/* 连接回调函数:处理新客户端连接 */
void conncb(evutil_socket_t lfd, short events, void *arg) {
    struct event_base *base = (struct event_base *)arg;
    int cfd = accept(lfd, NULL, NULL);  // 接受连接
    if (cfd > 0) {
        // 创建读事件(EV_PERSIST保持持久化)
        struct event* readev = event_new(base, cfd, 
            EV_READ | EV_PERSIST, readcb, NULL);
        event_add(readev, NULL);  // 加入事件循环
        setEventFd(cfd, readev);  // 记录到全局数组
    }  // 未处理accept失败的情况
}

int main() {
    init_ev_fd();  // 初始化全局数组
    
    // 创建TCP socket
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    
    // 设置端口复用(避免TIME_WAIT)
    int opt = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    
    // 绑定地址
    struct sockaddr_in serv;
    bzero(&serv, sizeof(serv));
    serv.sin_addr.s_addr = htonl(INADDR_ANY);  // 监听所有IP
    serv.sin_port = htons(8888);               // 端口8888
    serv.sin_family = AF_INET;
    bind(fd, (struct sockaddr*)&serv, sizeof(serv));
    
    listen(fd, 128);  // 开始监听(BACKLOG=128)
    
    // 创建事件基地(核心事件循环)
    struct event_base *base = event_base_new();
    if (!base) {
        cerr << "event_base_new failed" << endl;
        return -1;
    }
    
    // 创建监听socket的事件(EV_PERSIST保持持久化)
    struct event *listen_event = event_new(base, fd, 
        EV_READ | EV_PERSIST, conncb, base);
    if (!listen_event) {
        event_base_free(base);
        return -1;
    }
    event_add(listen_event, NULL);  // 注册事件
    
    event_base_dispatch(base);  // 进入事件循环(阻塞)
    
    // 清理资源
    event_base_free(base);
    close(fd);
    return 0;
}

二级目录

相关推荐
Decay__2 小时前
VMware 安装 SQL Server 双节点 AlwaysOn 可用性组教程 —— WSFC、AG 与侦听器完整部署(系列核心篇)
运维·数据库·sql·mysql·架构
测试界的小码农2 小时前
【Linux 命令实战(06):Linux 文件权限管理,掌握 chmod、chown、chgrp】
linux·运维·服务器
afumu2 小时前
Go 写一个只监听 127.0.0.1 的控制服务:鉴权、白名单、危险命令拦截
后端·安全·go
艺杯羹3 小时前
1Panel 装好后 ufw 仍是 inactive:一次服务器安全盲区复盘
服务器·数据库·安全·网络安全·系统架构·实战·1panel
johnjincaaaa3 小时前
猿人学第 18 题「jsvmp 洞察先机」逆向通关实录
javascript·后端
陈希1233 小时前
负载均衡与高可用:LVS、HAProxy、Keepalived 实战
运维·负载均衡·lvs
用户4702784004053 小时前
C语言编译与链接
后端·开源
你三大爷3 小时前
从 Hessian 迁移到 protostuff:深嵌套模型反序列化耗时与高并发类加载锁的根治实战
后端·架构
IT枫斗者枫哥3 小时前
子线程断言失败,JUnit 为什么还显示通过?
java·后端
一条破秋裤3 小时前
【1114】【并发控制】【笔记】
linux