使用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;
}