1 项目简介
采用QT6制作客户端,Linux C++实现后端。为用户提供了一个即时聊天平台。
项目地址:ChatForge: 一款基于Qt的网络聊天室,高仿wechat
视频讲解:C++项目推荐,QT项目推荐-仿微信聊天,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和端口。
注册账号要用数字:
4 Linux后端框架快速分析
在以下函数断点:
-
main
-
accept
-
recv
-
send
main函数位置
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.
accept监听位置
#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
recv调用位置
#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
核心:
taskThread
-> Session::recvMsg 后续重点看Session类。
Session::handleMsg核心函数
通过分析recvMsg 找到调用 Session::handleMsg,从名字上看他就是用来处理消息的。
```
int Session::handleMsg(json msg)
{
usleep(1000);
int cmd = msg.at("cmd");
//LOGINFO("handleMsg command:%s\n", cmd.c_str());
switch (cmd)
{
case cmd_regist: //注册消息
{
...
break;
}
case cmd_login: //登录消息
{
...
break;
}
case cmd_friend_search: //好友查找消息
{
...
break;
}
case cmd_add_friend_request: //添加好友请求
{
...
}
break;
case cmd_add_friend_response: //添加好友响应
{
...
break;
}
break;
case cmd_friend_list: //好友列表
{
...
break;
}
case cmd_friend_chat: //好友聊天
{...
break;
}
case cmd_group_create: //群创建
{
...
break;
}
case cmd_group_search: //群搜索
{
...
break;
}
case cmd_group_join_request: //加入群
{
...
break;
}
case cmd_group_join_response: //加群响应
{
...
break;
}
case cmd_group_list: //群列表
{
int account = msg.at("account");
CommandHandler::GroupList(account, this);
break;
}
case cmd_group_chat: //群聊天
{
...
break;
}
case cmd_group_member_list: //群成员
{
...
break;
}
case cmd_group_member_add: //群增加成员
{
...
break;
}
case cmd_group_member_del: //群删除成员
{
...
break;
}
case cmd_set_icon: //设置头像
{
...
break;
}
}
return 0;
}
send函数位置
send函数调用,在处理完消息后应答客户端时调用
#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
消息协议设计
消息格式:
-
[0, 3] 前4个字节封装了后续json字符串的长度length
-
[4, length+4-1].
命令类型
// 命令枚举
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
};
消息是通过json做序列化:
通过观察: rsvmsg = 分析服务器接收数据的信息,比如:
-
{"account":"120","cmd":1,"password":"1234"} ,对应为cmd_login,登录请求
-
{"account":120,"cmd":6},对应为cmd_friend_list,请求获取好友列表
通过观察 send: 分析服务器发送数据的信息,比如:
-
{"cmd":1,"info":["lys","hello",":/Icons/src/QQIcon/icon.jpg"],"res":"yes"} ,对应cmd_login的响应
-
{"cmd":6,"count":1,"msglist":[{"account":110,"icon":":/Icons/src/QQIcon/icon.jpg","name":"lqf","online":1,"sig":"hello"}]},对应cmd_friend_list请求获取好友列表的响应,好友在msglist里。
核心类Session
每个客户端绑定一个session,绑定了socket fd.