欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > 基于libevent写一个服务器(附带源码)

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

2025/5/4 18:07:14 来源:https://blog.csdn.net/m0_54069809/article/details/147125546  浏览:    关键词:基于libevent写一个服务器(附带源码)

使用libevent搭建服务器

  • 服务器源码
  • 处理逻辑

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

服务器源码

#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);              // 关闭socketevent_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 socketint 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);  // 监听所有IPserv.sin_port = htons(8888);               // 端口8888serv.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;
}

处理逻辑

1.全局数组管理​​
• 使用event[MAX]数组跟踪所有活跃连接的文件描述符(fd)和对应的事件对象(event)。
• init_ev_fd初始化数组,setEventFd存储新连接信息,findEv通过fd查找索引。
2. ​​事件驱动模型​​
• ​​监听事件​​:主socket(fd)注册conncb回调,接受新连接。
• ​​数据事件​​:每个客户端连接创建独立的readcb回调处理数据。
3. ​​回调函数链​​
• conncb → 接受连接 → 创建数据事件 → 加入事件循环。
• readcb → 读取数据 → 处理业务 → 响应客户端。
4. ​​资源管理​​
• 连接关闭时:关闭fd、移除事件、释放内存、重置数组项。
• 程序退出时:释放事件基地、关闭主socket。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词