在Linux环境下,高性能网络编程的核心在于高效利用系统资源、减少拷贝、优化事件处理机制 。本教程将带你从零实现一个基于Reactor模式+Epoll的C++网络框架,支持高并发(百万级连接)、低延迟(微秒级响应),并包含关键代码解析与完整实现步骤。
一、框架设计核心思想
-
Reactor模式:
- 主线程(Reactor)监听所有事件(如连接、读写),通过回调函数将事件分发给工作线程处理。
- 避免多线程竞争,减少锁开销。
-
Epoll边缘触发(ET) :
- 相比水平触发(LT),ET模式仅在文件描述符状态变化时通知,减少重复事件处理。
- 需配合非阻塞IO(
fcntl(fd, F_SETFL, O_NONBLOCK))使用。
-
内存池与零拷贝:
- 自定义内存池减少动态内存分配开销。
- 使用
sendfile系统调用实现文件传输零拷贝。
二、关键组件实现
1. 基础结构定义
arduino
cpp
1#include <sys/epoll.h>
2#include <fcntl.h>
3#include <unordered_map>
4#include <functional>
5
6// 事件回调类型定义
7using EventCallback = std::function<void(int fd)>;
8
9// 连接对象(封装socket描述符与回调)
10struct Connection {
11 int fd;
12 EventCallback onRead;
13 EventCallback onWrite;
14 // 其他业务数据(如缓冲区、状态等)
15};
16
17// Reactor核心类
18class Reactor {
19private:
20 int epoll_fd;
21 std::unordered_map<int, Connection> connections; // fd到连接的映射
22
23public:
24 Reactor() {
25 epoll_fd = epoll_create1(0); // 创建epoll实例
26 if (epoll_fd == -1) {
27 perror("epoll_create1 failed");
28 exit(EXIT_FAILURE);
29 }
30 }
31
32 ~Reactor() { close(epoll_fd); }
33
34 // 添加事件监听
35 void addEvent(int fd, uint32_t events, EventCallback onRead, EventCallback onWrite);
36
37 // 事件循环
38 void run();
39};
2. Epoll事件管理
ini
cpp
1void Reactor::addEvent(int fd, uint32_t events, EventCallback onRead, EventCallback onWrite) {
2 // 设置socket为非阻塞
3 int flags = fcntl(fd, F_GETFL, 0);
4 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
5
6 // 注册事件到epoll
7 struct epoll_event ev;
8 ev.events = events | EPOLLET; // 边缘触发
9 ev.data.fd = fd;
10
11 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
12 perror("epoll_ctl add failed");
13 close(fd);
14 return;
15 }
16
17 // 保存连接信息
18 connections[fd] = {fd, onRead, onWrite};
19}
3. 事件循环与回调分发
ini
cpp
1void Reactor::run() {
2 const int MAX_EVENTS = 1024;
3 struct epoll_event events[MAX_EVENTS];
4
5 while (true) {
6 int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); // 阻塞等待事件
7 if (n == -1) {
8 perror("epoll_wait failed");
9 break;
10 }
11
12 for (int i = 0; i < n; ++i) {
13 int fd = events[i].data.fd;
14 auto& conn = connections[fd];
15
16 if (events[i].events & EPOLLIN) { // 可读事件
17 if (conn.onRead) conn.onRead(fd);
18 }
19
20 if (events[i].events & EPOLLOUT) { // 可写事件
21 if (conn.onWrite) conn.onWrite(fd);
22 }
23
24 // 处理错误或挂断事件
25 if (events[i].events & (EPOLLERR | EPOLLHUP)) {
26 close(fd);
27 connections.erase(fd);
28 }
29 }
30 }
31}
三、完整服务端实现
1. 初始化与启动服务
c
cpp
1#include <iostream>
2#include <cstring>
3#include <unistd.h>
4#include <arpa/inet.h>
5
6// 简单的读回调:回显数据
7void onRead(int fd) {
8 char buf[1024];
9 ssize_t n = recv(fd, buf, sizeof(buf), 0);
10 if (n > 0) {
11 send(fd, buf, n, 0); // 回显
12 } else if (n == 0) { // 客户端关闭连接
13 close(fd);
14 }
15}
16
17// 简单的写回调(示例中未使用)
18void onWrite(int fd) { /* ... */ }
19
20int main() {
21 Reactor reactor;
22
23 // 创建监听socket
24 int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
25 struct sockaddr_in addr;
26 memset(&addr, 0, sizeof(addr));
27 addr.sin_family = AF_INET;
28 addr.sin_port = htons(8080);
29 addr.sin_addr.s_addr = INADDR_ANY;
30
31 bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));
32 listen(listen_fd, SOMAXCONN);
33
34 // 将监听socket设为非阻塞并添加到Reactor
35 int flags = fcntl(listen_fd, F_GETFL, 0);
36 fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK);
37
38 // 监听可读事件(新连接到达)
39 reactor.addEvent(listen_fd, EPOLLIN, [](int fd) {
40 struct sockaddr_in client_addr;
41 socklen_t len = sizeof(client_addr);
42 int client_fd = accept(fd, (struct sockaddr*)&client_addr, &len);
43 if (client_fd > 0) {
44 // 为新连接注册读事件(回显业务)
45 Reactor* reactor = Reactor::getInstance(); // 假设Reactor为单例
46 reactor->addEvent(client_fd, EPOLLIN, onRead, onWrite);
47 }
48 }, nullptr);
49
50 std::cout << "Server started on port 8080..." << std::endl;
51 reactor.run(); // 启动事件循环
52
53 return 0;
54}
2. 性能优化技巧
-
SO_REUSEPORT:允许多线程绑定同一端口,加速连接建立。
inicpp 1int opt = 1; 2setsockopt(listen_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); -
TCP_FASTOPEN:减少TCP握手延迟(需内核支持)。
scsscpp 1setsockopt(listen_fd, IPPROTO_TCP, TCP_FASTOPEN, &opt, sizeof(opt)); -
内存池优化 :预分配缓冲区池,避免频繁
malloc/free。
四、测试与验证
-
使用
wrk压测:arduinobash 1wrk -t4 -c1000 -d30s http://localhost:8080-t4:4个线程-c1000:1000个并发连接-d30s:持续30秒
-
监控系统指标:
netstat -anp | grep 8080:查看连接数top -p <pid>:观察CPU占用strace -p <pid>:跟踪系统调用
五、扩展功能建议
- 定时器支持 :集成
timerfd实现定时任务(如心跳检测)。 - 线程池:将耗时业务逻辑(如数据库查询)放到工作线程池处理。
- SSL/TLS加密:集成OpenSSL支持HTTPS。
六、总结
本教程实现了基于Reactor+Epoll的C++高性能网络框架,核心代码仅200余行,却具备以下特性:
- 百万级连接:通过Epoll边缘触发与非阻塞IO实现。
- 低延迟:减少事件处理中的冗余拷贝与系统调用。
- 易扩展:通过回调机制分离事件处理与业务逻辑。