一个基于 C++17 和 Linux Socket 的网络服务器练手项目,按照“阻塞式 TCP -> 非阻塞 + epoll -> 线程池”的路线逐步演进。
当前版本已经完成:
- 非阻塞监听 socket 与连接 socket
- 基于
epoll的事件驱动主循环 - 多客户端并发接入
Connection连接对象管理- 基于
\n分隔符的基础消息切分 - 最小线程池实现与任务投递
- worker 结果通过结果队列回到主线程
- 基于
eventfd的主线程唤醒 - Echo 回显与
quit主动退出
这个项目的重点不是一次性写成工业级高并发服务器,而是通过逐步演进,把下面这些核心概念真正跑通并讲清楚:
- Linux Socket 基础流程
- 非阻塞 I/O
epoll事件驱动模型- 连接对象管理
- 线程池与 I/O / 业务解耦
当前代码已经从最早的阻塞式 Echo Server 演进到一个可运行的 epoll 多连接 Echo Server,核心流程如下:
socket
->
bind
->
listen
->
epoll_create1
->
epoll_ctl(listen_fd)
->
epoll_wait
->
accept / recv
->
Connection buffer
->
ThreadPool
->
TaskResult queue
->
eventfd wakeup
->
write_buffer / send
- 主线程通过
epoll_wait统一等待事件 listen_fd负责接收新连接- 客户端连接通过
Connection对象保存状态 - 每个连接拥有独立的读写缓冲区
- 收到的数据先进入
read_buffer - 以
\n作为消息边界,逐条切分并处理 - 主线程把完整消息投递给线程池
- worker 线程生成响应结果
- 处理结果通过
TaskResult队列回到主线程 - worker 通过
eventfd唤醒主线程处理结果 - 主线程统一把结果写入
write_buffer并发送
- 写缓冲驱动的异步发送
- 更完善的日志系统
- 超时连接清理
- 压力测试
- C++17
- Linux Socket API
- epoll
- eventfd
- CMake
- TCP/IP
epoll-threadpool-server
├── include
│ ├── connection.h
│ ├── tcp_server.h
│ └── threadpool.h
├── src
│ ├── connection.cpp
│ ├── main.cpp
│ ├── tcp_server.cpp
│ └── threadpool.cpp
├── test
├── CMakeLists.txt
├── rebuild.sh
├── run.sh
└── README.md
include/:头文件,声明服务器与连接对象接口src/:源文件,包含主逻辑实现test/:测试代码目录run.sh:增量构建并运行rebuild.sh:清理后重新构建并运行
负责:
- 创建监听 socket
- 初始化
epoll - 事件分发
- 新连接接入
- 连接关闭清理
负责保存单个连接的状态:
fdread_bufferwrite_buffer
引入 Connection 后,服务器开始从“按 fd 处理”过渡到“按连接对象处理”,为后续解决粘包半包和引入线程池打下基础。
当前版本已经实现了一个最小线程池:
- 固定数量 worker 线程
- 任务队列
mutex + condition_variable- 析构时统一唤醒并
join
在服务器侧,主线程收到完整消息后会把任务投递给线程池。worker 线程只负责生成 response,再通过 TaskResult 队列把结果交还给主线程。
为了避免 worker 线程直接操作 socket 和连接表,当前版本采用了“结果队列”这一中间层:
HandleRead
->
Enqueue(task)
->
worker generate response
->
TaskResult queue
->
eventfd wakeup
->
ProcessTaskResults
->
Connection::write_buffer
->
send
这样主线程继续统一管理 connections_ 和 socket 生命周期,线程池只负责业务处理。
当前版本增加了一个 wake_fd,把它也注册进 epoll:
- worker 线程在 push 结果后向
eventfd写入计数 - 主线程从
epoll_wait中被唤醒 - 主线程在
HandleWakeFd()中读空eventfd - 然后调用
ProcessTaskResults()消费结果队列
这样可以避免“任务结果已经生成,但主线程还阻塞在下一轮 epoll_wait 里”的问题。
当前版本使用 \n 作为简单应用层分隔符:
- 一次
recv可能收到半条消息 - 也可能收到多条消息
- 数据先写入
Connection::read_buffer - 再循环查找
\n,逐条切分完整消息
这使项目具备了基础的粘包 / 半包处理能力。
./run.sh./rebuild.sh默认监听:
- IP:
0.0.0.0 - Port:
8080
使用 nc 连接:
nc 127.0.0.1 8080输入:
hello
world
quit
可以验证:
- 基础连接建立
- 多条消息逐条回显
quit主动退出
如果同时开启多个 nc 客户端,还可以验证多连接接入是否正常。
可以把当前版本定义为:
一个基于 C++17、非阻塞 socket、epoll、Connection 和最小线程池协作的多客户端 Echo Server 原型。
它已经具备:
- 事件驱动 I/O 模型
- 多连接接入
- 基础连接状态管理
- 简单的消息边界处理
- 线程池任务处理
- 结果队列回传
eventfd唤醒协作
下一步将继续完善写缓冲驱动发送、EPOLLOUT 写事件,以及更完整的线程池与业务解耦。
This project is for learning and practice.