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字节)      (变长)

协议实现细节

发送消息流程:

  1. 将JSON对象序列化为字符串
  2. 计算消息长度(字节数)
  3. 构造4字节长度头 + JSON消息体
  4. 通过socket发送完整数据包

接收消息流程:

  1. 先读取4字节获取消息长度
  2. 根据长度读取完整JSON数据
  3. 解析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关系图

关键设计特点

  1. 用户账号自增:account字段自动递增,确保唯一性
  2. 双向好友关系:friend表通过(user1,user2)复合主键存储好友关系
  3. 群组层级管理:群主通过group_master字段关联到user表
  4. 群昵称支持:成员在不同群组可以有不同昵称

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": "账号已存在"  // 失败时的错误信息
}

处理逻辑:

  1. 检查账号是否已存在
  2. 插入用户记录到user表
  3. 自动添加系统好友(10000)
  4. 返回注册结果

2. 用户登录 (cmd_login = 1)

客户端请求:

{
    "cmd": 1,
    "account": 12345,
    "password": "123456"
}

服务端响应:

{
    "cmd": 1,
    "res": "yes",
    "info": ["张三", "hello", ":/Icons/src/QQIcon/icon.jpg"]
}

处理逻辑:

  1. 验证账号密码
  2. 更新在线状态为1
  3. 将连接加入userMap
  4. 返回用户基本信息

好友管理类命令

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客户端

当前限制

  • 并发性能:一连接一线程模型,高并发时线程开销大
  • 消息持久化:离线消息未存储,用户离线时消息丢失
  • 负载均衡:单机部署,无集群支持
  • 安全性:明文传输,缺少加密和认证机制

优化建议

  1. 引入线程池:减少线程创建销毁开销
  2. 消息队列:支持离线消息存储和推送
  3. 连接池:优化数据库连接管理
  4. SSL/TLS:加密网络传输
  5. Redis缓存:提升在线状态查询性能

5 项目总结

5.1 技术栈总览

5.2 架构优势

  1. 开发效率高:基于成熟开源库,快速原型开发
  2. 部署简单:单机部署,无复杂依赖
  3. 易于调试:架构清晰,调试断点明确
  4. 跨平台性:客户端支持Windows/Linux

5.3 适用场景

  • 学习项目:理解网络编程和数据库操作
  • 小型团队:内部沟通工具(<100人)
  • 原型验证:快速验证聊天功能需求
  • 技术演示:展示Qt + C++技术栈

5.4 扩展方向

功能扩展

  • 文件传输、图片分享
  • 语音/视频通话
  • 消息加密、数字签名
  • 离线消息、消息历史

架构升级

  • 微服务化、负载均衡
  • 消息队列、缓存层
  • 容器化部署
  • 监控告警体系

这个项目为即时通讯系统提供了一个完整的基础框架,适合作为学习和进一步开发的起点。

#校招过来人的经验分享##校招##项目##c++##简历中的项目经历要怎么写#
全部评论

相关推荐

08-15 17:27
已编辑
华南师范大学 C++
2025/8/14&nbsp;字节客户端一面:自我介绍项目10分钟在学习/项目中,最有挑战的事情是什么操作系统1.进程和线程的理解2.进程间的通信:管道,消息队列,信号/信号量计网:3.http和https的优缺点4.https建立连接的流程:TCP连接,最后发送密钥5.密钥是从哪里获取的6.数据传输时用的什么加密方式:对称加密7.为什么不用非对称加密:资源消耗,密钥发送安全性,面试官补充时效性?数据库:8.数据库索引有什么用,什么时候建立索引,建立在什么字段上,什么时候适用什么时候不适用?C++:9.C++和其他语言在设计上有什么区别?10.C++中没有interface字段,c++用什么来定义接口:应回答抽象类11.C++抽象类和普通类的区别12.虚函数和纯虚函数有什么区别在c++中,现有类A和类B,在A和B中声明了一个完全相同的虚函数,现有一个类c,c继承A和B,那么在c中能否调用A和B声明的哪个虚函数,如何确定调用的是A还是B的虚函数:(1)如果C没有覆盖该虚函数,直接调用会编译失败(歧义错误)(2)通过作用域解析运算符::显式指定调用哪个基类的版本(3)若C覆盖了该函数,调用c.func()会执行C的版本,但仍可通过作用域调用基类版本(4)通过基类指针调用时,行为由指针类型决定13.Std::move和普通的赋值语句有什么区别14.String&nbsp;A=“asdasd”&nbsp;string&nbsp;B=move(A)&nbsp;之后还可以访问A吗15.什么场景下会用到shared_ptr16.shared_ptr的循环引用怎么解决17.线程安全的问题会出现在什么场景下18.乐观锁和悲观锁:一个在循环检测,一个不循环检测19.除了c++还会用其他语言么20.平时会用AI去帮忙写代码么手撕,二叉树,判断是否存在一条路径,所有节点的和等于目标值,从根节点出发求求给个机会!&nbsp;8.15已挂
查看25道真题和解析
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务