C++ QT项目推荐-仿微信聊天,支持Linux C++后端
1 项目简介
采用QT6制作客户端,Linux C++实现后端。为用户提供了一个即时聊天平台。
项目地址:https://gitee.com/voice-of-sentiment/chat-forge.git
视频讲解与源码领取:C++ QT项目推荐-仿微信聊天实现添加好友,一对一和群聊,支持Linux C++后端
2 Linux C++后端编译和运行
开源项目地址
git clone https://gitee.com/voice-of-sentiment/chat-forge.git cd chat-forge/server/thirdparty git clone https://gitee.com/NEU-lab/SQLiteCpp.git # 如果用老廖提供的源码,直接解压源码包就行 #回到chat-forge/server目录 cd .. cd build rm -rf * #重新cmake 编译debug方式 cmake -DCMAKE_BUILD_TYPE=Debug .. make -j4
编译成功后产生server执行文件。
运行:
./server
默认监听端口为:8888
3 QT客户端编译和运行
编译环境:QT6.5 MinGW 64-bit
运行代码前修改服务器地址的ip和端口。
注册账号要用数字:
再开启一个客户端
用法很简单:在 Qt 提供的命令行里对 exe 运行 windeployqt,把需要的 Qt 插件/DLL 拷到同目录即可。
步骤
- 打开“Qt 6.5.0 for Desktop (MinGW 64-bit)”命令提示符(保证 windeployqt 在 PATH 中)。
- 进入你的 exe 目录:
# 先切换盘符 F: # 进入到具体路径 cd F:\0voice\vip\tc\202412\build-OurChat-Desktop_Qt_6_5_0_MinGW_64_bit-Debug\debug
- 执行部署(Debug 构建):
windeployqt --verbose 2 --no-translations OurChat.exe
- 如果是 Release 构建,进入 release 目录,执行:
windeployqt --release --verbose 2 --no-translations OurChat.exe
- 执行后,exe 目录应出现 platforms\qwindows.dll、styles\...、imageformats\... 等子目录及多个 Qt6*.dll。
- 双击 OurChat.exe 启动;若提示缺少 DLL,多半是 MinGW 运行库未拷贝或混用了 Debug/Release 库。
4 Linux后端架构详解
4.1 服务端整体架构
服务端采用多线程 + 会话管理 + 命令分发的架构模式,为每个客户端连接分配独立的处理线程。
核心组件说明
线程模型
服务端采用一连接一线程模型:
- 主线程负责监听和接受连接
- 每个客户端连接分配独立的工作线程
- 线程间通过互斥锁保护共享资源
架构流程图
4.2 网络通信协议
消息格式设计
采用长度前缀 + JSON消息体的二进制协议:
[0-3字节] [4字节开始] 消息长度 JSON消息体 (4字节) (变长)
协议实现细节
发送消息流程:
- 将JSON对象序列化为字符串
- 计算消息长度(字节数)
- 构造4字节长度头 + JSON消息体
- 通过socket发送完整数据包
接收消息流程:
- 先读取4字节获取消息长度
- 根据长度读取完整JSON数据
- 解析JSON并分发到对应处理函数
// 发送示例代码 void Session::sendMsg(json &j) { std::string msg = j.dump(); int len = msg.length(); char buffer[4]; memcpy(buffer, &len, sizeof(len)); char *message = new char[4 + len]; memcpy(message, buffer, 4); // 长度头 memcpy(message + 4, msg.c_str(), len); // JSON体 send(m_socket, message, len + 4, 0); }
4.3 数据库设计
数据表详细说明
数据库ER关系图
关键设计特点
- 用户账号自增:account字段自动递增,确保唯一性
- 双向好友关系:friend表通过(user1,user2)复合主键存储好友关系
- 群组层级管理:群主通过group_master字段关联到user表
- 群昵称支持:成员在不同群组可以有不同昵称
4.4 命令系统详解
命令枚举定义
enum commands { cmd_regist = 0, // 用户注册 cmd_login, // 用户登录 cmd_logout, // 用户登出 cmd_friend_search, // 搜索好友 cmd_add_friend_request, // 添加好友请求 cmd_add_friend_response, // 添加好友响应 cmd_friend_list, // 获取好友列表 cmd_friend_chat, // 好友聊天 cmd_group_create, // 创建群组 cmd_group_search, // 搜索群组 cmd_group_join_request, // 加入群组请求 cmd_group_join_response, // 加入群组响应 cmd_group_list, // 获取群组列表 cmd_group_chat, // 群组聊天 cmd_group_member_list, // 获取群成员列表 cmd_group_member_add, // 添加群成员 cmd_group_member_del, // 删除群成员 cmd_set_icon // 设置头像 };
命令处理流程图
4.5 核心命令详解
用户认证类命令
1. 用户注册 (cmd_regist = 0)
客户端请求:
{ "cmd": 0, "account": 12345, "password": "123456", "name": "张三" }
服务端响应:
{ "cmd": 0, "res": "yes", // 或 "no" "err": "账号已存在" // 失败时的错误信息 }
处理逻辑:
- 检查账号是否已存在
- 插入用户记录到user表
- 自动添加系统好友(10000)
- 返回注册结果
2. 用户登录 (cmd_login = 1)
客户端请求:
{ "cmd": 1, "account": 12345, "password": "123456" }
服务端响应:
{ "cmd": 1, "res": "yes", "info": ["张三", "hello", ":/Icons/src/QQIcon/icon.jpg"] }
处理逻辑:
- 验证账号密码
- 更新在线状态为1
- 将连接加入userMap
- 返回用户基本信息
好友管理类命令
3. 搜索好友 (cmd_friend_search = 3)
客户端请求:
{ "cmd": 3, "info": "12345" // 搜索的账号或昵称 }
服务端响应:
{ "cmd": 3, "count": 1, "msglist": [ { "account": 12345, "name": "张三", "signature": "hello", "online": 1, "icon": ":/Icons/src/QQIcon/icon.jpg" } ] }
4. 获取好友列表 (cmd_friend_list = 6)
客户端请求:
{ "cmd": 6, "account": 12345 }
服务端响应:
{ "cmd": 6, "count": 2, "msglist": [ { "account": 10000, "name": "系统消息", "signature": "系统官方账号", "online": 1, "icon": ":/Icons/src/QQIcon/icon.jpg" }, { "account": 54321, "name": "李四", "signature": "在线状态", "online": 0, "icon": ":/Icons/src/QQIcon/icon.jpg" } ] }
5. 好友聊天 (cmd_friend_chat = 7)
客户端请求:
{ "cmd": 7, "account": 12345, // 发送者账号 "friendAccount": 54321, // 接收者账号 "sendmsg": "你好!" // 消息内容 }
服务端处理:
- 转发消息给在线的目标好友
- 如果好友离线,消息暂不存储
群组管理类命令
6. 搜索群组 (cmd_group_search = 9)
客户端请求:
{ "cmd": 9, "info": "群名称或群号" }
服务端响应:
{ "cmd": 9, "count": 1, "msglist": [ { "group_account": 100001, "group_name": "技术交流群", "group_master": 12345 } ] }
7. 获取群组列表 (cmd_group_list = 12)
客户端请求:
{ "cmd": 12, "account": 12345 }
服务端响应:
{ "cmd": 12, "count": 1, "msglist": [ { "group_account": 100001, "group_name": "技术交流群" } ] }
8. 群组聊天 (cmd_group_chat = 13)
客户端请求:
{ "cmd": 13, "account": 12345, "groupAccount": 100001, "sendmsg": "大家好!" }
服务端处理:
- 获取群组所有在线成员
- 广播消息给所有在线群成员
- 排除发送者本人
4.6 GDB网络调试详解
网络框架调试流程图
关键断点设置策略
为什么在这些位置打断点?
详细调试步骤
1. main函数调试 - 服务器启动
设置断点:
(gdb) b main Note: breakpoint 1 also set at pc 0x5555555782a2. Breakpoint 4 at 0x5555555782a2: file /home/lqf/linux/reactor/chat-forge/server/main.cpp, line 13.
调试要点:
- 检查数据库初始化:SQLite::Database db("user.db")
- 验证表创建:CREATE TABLE IF NOT EXISTS user...
- 确认端口设置:default_port = 8888
- 观察socket创建:listen_fd = socket(AF_INET, SOCK_STREAM, 0)
关键变量监控:
(gdb) p default_port # 监控端口号 (gdb) p listen_fd # 监控socket文件描述符 (gdb) info locals # 查看所有局部变量
2. accept调试 - 连接监听
设置断点:
(gdb) b accept # 或者更精确地在main.cpp的accept调用处 (gdb) b main.cpp:88
调用栈分析:
#0 __libc_accept (fd=4, addr=..., len=0x7fffffffde78) at ../sysdeps/unix/sysv/linux/accept.c:24 #1 0x00005555555784f1 in main (argc=1, argv=0x7fffffffdfe8) at /home/lqf/linux/reactor/chat-forge/server/main.cpp:88
调试要点:
- 服务器进入监听状态,等待客户端连接
- 观察客户端连接信息:IP地址和端口
- 监控新连接的socket文件描述符
- 验证线程创建和userMap更新
关键变量监控:
(gdb) p client_addr.sin_addr # 客户端IP (gdb) p ntohs(client_addr.sin_port) # 客户端端口 (gdb) p connect_fd # 新连接的socket fd (gdb) p userMap # 在线用户映射表
3. recv调试 - 数据接收
设置断点:
(gdb) b Session::recvMsg # 或者在具体的recv系统调用处 (gdb) b session.cpp:301
调用栈分析:
#0 __libc_recv (fd=6, buf=0x7ffff71a0be4, len=4, flags=0) at ../sysdeps/unix/sysv/linux/recv.c:24 #1 in Session::recvMsg[abi:cxx11]() (this=) at /home/lqf/linux/reactor/chat-forge/server/session.cpp:301 #2 in taskThread (clientFd=6) at /home/lqf/linux/reactor/chat-forge/server/chatTask.cpp:13 #3 in std::__invoke_impl<void, void (*)(int), int> (__f=@0x55555570cd80: <taskThread(int)>) at /usr/include/c++/10/bits/invoke.h:60 #4 in std::__invoke<void (*)(int), int> (__fn=@0x55555570cd80: 0x55555557455f <taskThread(int)>) at /usr/include/c++/10/bits/invoke.h:95 #5 in std::thread::_Invoker<std::tuple<void (*)(int), int> >::_M_invoke<0ul, 1ul> (this=) at /usr/include/c++/10/thread:264 #6 in std::thread::_Invoker<std::tuple<void (*)(int), int> >::operator() (this=) at /usr/include/c++/10/thread:271 #7 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(int), int> > >::_M_run (this=) at /usr/include/c++/10/thread:215 #8 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6 #9 in start_thread (arg=<optimized out>) at pthread_create.c:477 #10 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
调试要点:
- 首先接收4字节长度头:recv(m_socket, buffer, 4, 0)
- 然后接收完整JSON数据:recv(m_socket, msg, len, MSG_WAITALL)
- 验证协议格式:长度前缀 + JSON消息体
- 观察JSON解析过程:json::parse(msg)
关键变量监控:
(gdb) p len # 消息长度 (gdb) x/4xb buffer # 查看4字节长度头的十六进制 (gdb) p msg # JSON字符串内容 (gdb) p j # 解析后的JSON对象 (gdb) p j["cmd"] # 命令类型
协议验证:
# 查看原始字节数据 (gdb) x/16xb buffer # 十六进制查看 (gdb) printf "%s\n", msg # 打印JSON字符串
4. send调试 - 响应发送
设置断点:
(gdb) b Session::sendMsg # 或者在具体的send系统调用处 (gdb) b session.cpp:244
调用栈分析:
#0 __libc_send (fd=6, buf=0x7fffe8001180, len=78, flags=0) at ../sysdeps/unix/sysv/linux/send.c:24 #1 0x000055555557c66a in Session::sendMsg (this=, j=...) at /home/lqf/linux/reactor/chat-forge/server/session.cpp:244 #2 0x0000555555562e49 in CommandHandler::Login (account=110, password="1234", session=) at /home/lqf/linux/reactor/chat-forge/server/CommandHandler.cpp:65 #3 0x000055555557b088 in Session::handleMsg (this=, msg=...) at /home/lqf/linux/reactor/chat-forge/server/session.cpp:58 #4 0x000055555557ccb9 in Session::recvMsg[abi:cxx11]() (this=) at /home/lqf/linux/reactor/chat-forge/server/session.cpp:334 #5 0x00005555555745d6 in taskThread (clientFd=6) at /home/lqf/linux/reactor/chat-forge/server/chatTask.cpp:13
调试要点:
- 构造响应JSON:j.dump()
- 发送长度头:send(m_socket, message, 4, 0)
- 发送JSON数据:send(m_socket, message+4, len, 0)
- 验证发送状态和数据完整性
关键变量监控:
(gdb) p j # 响应JSON对象 (gdb) p msg # JSON字符串 (gdb) p len # 消息长度 (gdb) x/4xb buffer # 长度头的十六进制 (gdb) printf "%s\n", msg.c_str() # 响应内容
完整调试会话示例
# 启动GDB调试 $ gdb ./server (gdb) b main (gdb) b accept (gdb) b Session::recvMsg (gdb) b Session::sendMsg (gdb) run # 当客户端连接时 Breakpoint 2, accept (...) (gdb) p client_addr.sin_addr (gdb) c # 当客户端发送消息时 Breakpoint 3, Session::recvMsg (...) (gdb) p len (gdb) p msg (gdb) p j["cmd"] (gdb) c # 当服务器发送响应时 Breakpoint 4, Session::sendMsg (...) (gdb) p j (gdb) printf "%s\n", msg.c_str() (gdb) c
调试最佳实践
常见问题排查
1.连接失败
- 检查端口是否被占用:netstat -tlnp | grep 8888
- 验证防火墙设置:iptables -L
- 确认服务器监听状态:ss -tlnp | grep 8888
2.消息解析错误
- 验证JSON格式:使用在线JSON验证工具
- 检查字符编码:确保UTF-8编码
- 监控消息长度:长度头与实际数据是否匹配
3.数据库操作失败
- 检查数据库文件权限:ls -la user.db
- 验证SQL语句:使用sqlite3命令行工具测试
- 监控数据库锁:检查并发访问冲突
性能调优断点
# 监控线程创建 (gdb) b pthread_create # 监控数据库操作 (gdb) b SQLite::Statement::executeStep # 监控内存分配 (gdb) b malloc (gdb) b free
4.7 性能特点与限制
优势
- 简单直观:架构清晰,易于理解和维护
- 快速开发:基于成熟的SQLite和nlohmann/json库
- 跨平台:Linux服务端 + Windows/Linux客户端
当前限制
- 并发性能:一连接一线程模型,高并发时线程开销大
- 消息持久化:离线消息未存储,用户离线时消息丢失
- 负载均衡:单机部署,无集群支持
- 安全性:明文传输,缺少加密和认证机制
优化建议
- 引入线程池:减少线程创建销毁开销
- 消息队列:支持离线消息存储和推送
- 连接池:优化数据库连接管理
- SSL/TLS:加密网络传输
- Redis缓存:提升在线状态查询性能
5 项目总结
5.1 技术栈总览
5.2 架构优势
- 开发效率高:基于成熟开源库,快速原型开发
- 部署简单:单机部署,无复杂依赖
- 易于调试:架构清晰,调试断点明确
- 跨平台性:客户端支持Windows/Linux
5.3 适用场景
- 学习项目:理解网络编程和数据库操作
- 小型团队:内部沟通工具(<100人)
- 原型验证:快速验证聊天功能需求
- 技术演示:展示Qt + C++技术栈
5.4 扩展方向
功能扩展
- 文件传输、图片分享
- 语音/视频通话
- 消息加密、数字签名
- 离线消息、消息历史
架构升级
- 微服务化、负载均衡
- 消息队列、缓存层
- 容器化部署
- 监控告警体系
这个项目为即时通讯系统提供了一个完整的基础框架,适合作为学习和进一步开发的起点。
#校招过来人的经验分享##校招##项目##c++##简历中的项目经历要怎么写#