NebulaChat:C++ 高并发聊天室服务端

项目地址: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 / accept

    • epoll 多路复用

    • 非阻塞 I/O

    • TCP_NODELAY

  • 并发相关:

    • std::thread

    • std::mutex / std::unique_lock / std::condition_variable

    • std::atomic<bool> 控制连接写状态、短关标记等

  • 存储:

    • MySQL(libmysqlclient

    • Redis(hiredis

  • 第三方库:

    • nlohmann::json 处理 JSON 协议

三、项目目录结构示例

bash 复制代码
.
├── 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,设置非阻塞和端口复用,加入 Reactor

    • onAccept():接受新连接,创建 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> wantWrite

    • std::atomic<bool> shortClose

  • 会话状态:

    • bool authed:是否已登录

    • int userId:用户 ID

    • std::string name:用户名

    • int roomId:所在房间号

ThreadPool 与 SafeQueue
  • SafeQueue<T>

    • 使用 std::mutexstd::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 操作 MySQL

    • **RedisPool**做用户缓存与空值缓存,减少 DB 压力

SmsService
  • 模拟短信验证码服务:

    • 生成随机验证码

    • 将验证码写入 Redis,key 类似 sms:phone:<手机号>

    • 同时写日志,方便在终端查看验证码

  • 校验逻辑:

    • 从 Redis 中读取验证码并比对

    • 可设置一次性使用和过期时间

RoomManager
  • 管理房间在线人数:

    • tryEnterRoom(roomId, maxSize) :若当前人数小于 maxSize,则人数加一并返回 true

    • leaveRoom(roomId):用户离开房间时人数减一

    • getRoomSize(roomId):查询当前房间在线人数

  • 内部使用:

    • std::mutex

    • std::unordered_map<int, int> 存储房间人数

ChatHistory
  • SaveMessage()

    • 将单条消息写入 MySQL messages 表,字段包括:

      • room_id

      • user_id

      • username

      • content

      • created_at

    • 使用 mysql_real_escape_string 转义字符串,避免 SQL 注入和语法错误

  • GetHistoryWithCache(roomId, limit, historyOut)

    • Redis 正常:

      1. 尝试 GET room:history:<roomId>:<limit>

      2. 命中缓存则直接解析返回

      3. 未命中:

        • 使用互斥锁 + 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. 用户名 + 密码登录

请求:

bash 复制代码
{"cmd": "login", "mode": "password", "user": "elias", "pass": "123456"}

成功响应示例:

bash 复制代码
{
  "ok": true,
  "roomId": 1
}

2. 发送消息

请求:

bash 复制代码
{"cmd": "send_msg", "text": "hello, everyone"}

广播给房间内所有用户的响应示例:

bash 复制代码
{
  "ok": true,
  "broadcast": true,
  "roomId": 1,
  "fromId": 1001,
  "fromName": "elias",
  "text": "hello, everyone",
  "ts": 1732250000
}

3. 加入或切换房间

请求:

bash 复制代码
{"cmd": "join_room", "roomId": 2}

成功响应:

bash 复制代码
{"ok": true, "roomId": 2, "msg": "join room success"}

房间已满时的响应:

bash 复制代码
{"ok": false, "roomId": 1, "msg": "room is full"}

4. 拉取历史消息

请求:

bash 复制代码
{"cmd": "get_history", "limit": 20}

响应示例:

bash 复制代码
{
  "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 服务器和开发库:

    • 示例安装命令:

      bash 复制代码
      sudo apt install mysql-server libmysqlclient-dev
  • Redis 服务器和 hiredis:

    • 示例安装命令:

      bash 复制代码
      sudo apt install redis-server libhiredis-dev

2. 配置数据库与 Redis

DBpool.cppRedisPool.cpp 中配置:

  • host

  • user

  • password

  • database

  • port

确保:

  • MySQL 中已创建所需数据库和数据表(例如 usersmessages

  • Redis 已启动,密码和端口与代码配置一致

3. 编译与运行

bash 复制代码
mkdir build
cd build
cmake ..
make -j

编译成功后执行:

bash 复制代码
./NebulaChat
相关推荐
haofafa1 小时前
JavaScript性能优化实战
开发语言·javascript·性能优化
帅中的小灰灰1 小时前
C++编程策略设计模式
开发语言·c++·设计模式
c***72741 小时前
【Redis系列】RedisTemplate的使用与注意事项
数据库·redis·缓存
O***p6041 小时前
JavaScript增强现实开发
开发语言·javascript·ar
Antonio9152 小时前
【Swift】Swift基础语法:函数、闭包、枚举、结构体、类与属性
开发语言·swift
csbysj20202 小时前
Vue3 事件处理
开发语言
h***38182 小时前
SQL 注入漏洞原理以及修复方法
网络·数据库·sql
Q***f6352 小时前
Kotlin在Android性能优化中的工具
android·开发语言·kotlin
是小胡嘛2 小时前
华为云CentOS系统中运行http服务器无响应
linux·服务器·c++·http·centos·华为云