项目地址:https://github.com/elaysia-feng/NebulaChat.git
基于 C++17 的高并发聊天室服务端,是一个涵盖现代 C++、Linux 网络编程、MySQL、Redis 的综合项目。
一、功能概览
1. 用户系统
用户名 + 密码登录
手机号 + 短信验证码登录(模拟短信服务)
用户注册(手机号 + 验证码 + 用户名 + 密码)
忘记密码(手机号 + 验证码重置密码)
登录后修改昵称
2. 聊天系统
多聊天室房间(
roomId)登录后自动加入默认房间(如 1 号房)
房间容量限制(如
MAX_ROOM_SIZE = 100)房间内广播消息:一人发言,房间内所有在线用户收到
拉取历史消息,支持指定条数
limit
3. 存储与缓存
MySQL 持久化:
用户信息
聊天消息
Redis 缓存:
用户缓存(用户名 / 手机号 → 用户信息或 null)
短信验证码
聊天历史缓存(按房间 + limit 维度)
缓存策略:
空值缓存:不存在的用户结果会缓存
"null"历史消息:互斥锁 + double-check,避免热点 key 失效时的缓存击穿
历史缓存 TTL 加随机抖动,减轻缓存雪崩
4. 并发模型
单 Reactor(
epoll)+ 多线程线程池非阻塞 socket,支持边缘触发和水平触发
使用
eventfd唤醒 Reactor线程安全任务队列
SafeQueue会话结构
Connection:记录用户登录状态、房间号、输入输出缓冲等
二、技术栈
语言:C++17
平台:Linux(如 Ubuntu 20.04+)
构建工具:CMake
网络编程:
socket/bind/listen/acceptepoll多路复用非阻塞 I/O
TCP_NODELAY
并发相关:
std::threadstd::mutex/std::unique_lock/std::condition_variablestd::atomic<bool>控制连接写状态、短关标记等
存储:
MySQL(
libmysqlclient)Redis(
hiredis)
第三方库:
nlohmann::json处理 JSON 协议
三、项目目录结构示例
.
├── CMakeLists.txt
├── src
│ ├── main.cpp
│ ├── core
│ │ ├── Reactor.cpp
│ │ ├── Server.cpp
│ │ ├── ThreadPool.cpp
│ ├── chat
│ │ ├── MessageHandler.cpp
│ │ ├── AuthService.cpp
│ │ ├── SmsService.cpp
│ │ ├── ChatHistory.cpp
│ │ ├── RoomManager.cpp
│ ├── db
│ │ ├── DBconnection.cpp
│ │ ├── DBpool.cpp
│ │ ├── RedisConnection.cpp
│ │ ├── RedisPool.cpp
│ ├── utils
│ ├── Random.cpp
│ └── ...
└── include
├── core/*.h
├── chat/*.h
├── db/*.h
└── utils/*.h
四、核心设计
1. 网络层(core)
Reactor
封装
epoll负责:
注册 / 修改 / 删除 fd 事件
主循环
loop()处理
eventfd唤醒分发回调(
dispatcher_)
Server
管理监听 fd 和所有客户端连接
主要职责:
start():创建 socket,设置非阻塞和端口复用,加入 ReactoronAccept():接受新连接,创建Connection,放入conns_容器,注册读事件onConnRead():非阻塞读取数据到
Connection::inbuf按
\n拆包(行协议)将每条 JSON 请求放入线程池异步处理
postWrite(fd, data):线程安全地将响应追加到
Connection::outbuf打开该 fd 的
EPOLLOUT事件调用
reactor_.wakeup()唤醒事件循环
onConnWrite():非阻塞写
outbuf到 socket写完关闭
EPOLLOUT若标记
shortClose,则在写完后关闭连接
broadcastToRoom(roomId, data):遍历
conns_中所有连接按
Connection.roomId筛选房间内用户,逐个调用postWrite
Connection(namespace utils)
int fd:socket 描述符std::string inbuf:输入缓冲区std::string outbuf:输出缓冲区I/O 状态:
std::atomic<bool> wantWritestd::atomic<bool> shortClose
会话状态:
bool authed:是否已登录int userId:用户 IDstd::string name:用户名int roomId:所在房间号
ThreadPool 与 SafeQueue
SafeQueue<T>:使用
std::mutex和std::condition_variable实现的阻塞队列提供
Safepush/Safepop接口
ThreadPool:维护若干工作线程
从任务队列中取任务执行
Server::onConnRead中将业务处理逻辑投递到线程池
2. 业务层(chat)
MessageHandler
根据请求 JSON 中的
cmd字段分发业务逻辑:login:用户名密码登录或短信登录register:短信校验后注册用户reset_pass:手机号 + 验证码重置密码update_name:修改昵称join_room:加入或切换聊天室(带房间容量限制)send_msg:发送消息(房间广播 + 写入历史)get_history:拉取历史消息(使用缓存)
通信协议为一行 JSON,以
\n结尾。
AuthService
登录相关逻辑:
用户名 + 密码登录
手机号 + 短信验证码登录
注册用户
修改用户名
重置密码
依赖:
DBPool操作 MySQLRedisPool做用户缓存与空值缓存,减少 DB 压力
SmsService
模拟短信验证码服务:
生成随机验证码
将验证码写入 Redis,key 类似
sms:phone:<手机号>同时写日志,方便在终端查看验证码
校验逻辑:
从 Redis 中读取验证码并比对
可设置一次性使用和过期时间
RoomManager
管理房间在线人数:
tryEnterRoom(roomId, maxSize):若当前人数小于maxSize,则人数加一并返回 trueleaveRoom(roomId):用户离开房间时人数减一getRoomSize(roomId):查询当前房间在线人数
内部使用:
std::mutexstd::unordered_map<int, int>存储房间人数
ChatHistory
SaveMessage():将单条消息写入 MySQL
messages表,字段包括:room_iduser_idusernamecontentcreated_at
使用
mysql_real_escape_string转义字符串,避免 SQL 注入和语法错误
GetHistoryWithCache(roomId, limit, historyOut):Redis 正常:
尝试
GET room:history:<roomId>:<limit>命中缓存则直接解析返回
未命中:
使用互斥锁 + double-check 避免多个线程同时回源
从 DB 拉取最近
limit条消息写回 Redis,TTL 基础值加随机抖动
Redis 不可用:
进入降级模式
使用简单限流及互斥锁串行访问 DB,避免瞬间大量请求压垮数据库
3. 基础设施层(db 与 utils)
DBPool / DBConnection
提供简单的 MySQL 连接池
封装:
MYSQL_RES* query(const std::string& sql)bool update(const std::string& sql)
连接参数(host、user、password、db、port)在代码中配置
RedisPool / RedisConnection
基于
hiredis封装的 Redis 连接池提供:
bool get(const std::string& key, std::string& out)bool setEX(const std::string& key, const std::string& value, int ttl)
utils::Random
随机数工具:
int RandInt(int min, int max):使用线程本地mt19937和均匀分布生成区间随机整数
五、通信协议示例(JSON 行协议)
所有请求和响应统一为:一行 JSON 字符串 + 换行符 \n。
1. 用户名 + 密码登录
请求:
{"cmd": "login", "mode": "password", "user": "elias", "pass": "123456"}
成功响应示例:
{
"ok": true,
"roomId": 1
}
2. 发送消息
请求:
{"cmd": "send_msg", "text": "hello, everyone"}
广播给房间内所有用户的响应示例:
{
"ok": true,
"broadcast": true,
"roomId": 1,
"fromId": 1001,
"fromName": "elias",
"text": "hello, everyone",
"ts": 1732250000
}
3. 加入或切换房间
请求:
{"cmd": "join_room", "roomId": 2}
成功响应:
{"ok": true, "roomId": 2, "msg": "join room success"}
房间已满时的响应:
{"ok": false, "roomId": 1, "msg": "room is full"}
4. 拉取历史消息
请求:
{"cmd": "get_history", "limit": 20}
响应示例:
{
"ok": true,
"roomId": 1,
"history": [
{
"id": 123,
"roomId": 1,
"fromId": 1001,
"fromName": "elias",
"text": "hello",
"ts": 1732250000
}
]
}
六、构建与运行
1. 环境依赖
Linux(推荐 Ubuntu 20.04 及以上)
g++(支持 C++17)
CMake 3.10 及以上
MySQL 服务器和开发库:
示例安装命令:
sudo apt install mysql-server libmysqlclient-dev
Redis 服务器和 hiredis:
示例安装命令:
sudo apt install redis-server libhiredis-dev
2. 配置数据库与 Redis
在 DBpool.cpp 和 RedisPool.cpp 中配置:
hostuserpassworddatabaseport
确保:
MySQL 中已创建所需数据库和数据表(例如
users、messages)Redis 已启动,密码和端口与代码配置一致
3. 编译与运行
mkdir build
cd build
cmake ..
make -j
编译成功后执行:
./NebulaChat
浙公网安备 33010602011771号