C++聊天集群服务器解决客户端注销登录问题

发布时间 2023-12-20 11:25:31作者: 桂洛克船长

客户端如何处理注销登录问题?

问题描述:

​ 在客户端登录后进行注销选择,然后重新登录刚才注销的账号,直接卡死。注意这是概率发生,因为是主线程和子线程抢服务器发送的信息,只有子线程抢到才会发生卡死

问题产生原因分析:

image-20231219221403259

image-20231219221732886

​ 前置条件:主线程循环等待用户输入选择(第一张图是死循环,send后立马recv),子线程阻塞等待服务器数据。

​ 在客户端注销账号之前,我们启动了一个子线程来充当接收线程,阻塞等待服务器数据。当客户端注销账号后,并没有立马关闭子线程,然后立马重新登录,这时主线程会组装一个json数据发给服务器,服务器处理完后发送处理结果回客户端,此时当子线程竞争过主线程,抢到recv接收数据,但是子线程并没有处理相应的措施,因此一次循环后马上又阻塞在recv函数,而主线程因为一直未收到数据,因仍然阻塞在recv函数,所以造成了客户端死机的现象。

排查过程:

​ 使用命令

gdb attach pid(阻塞的进程id号)

​ 进入调试状态,然后

info threads 

​ 查看客户端的两个线程信息,发现两个线程都阻塞在recv函数上然后使用命令

bt

​ 打印线程调用堆栈,发现主线程阻塞在send函数后紧接的recv函数上,切换线程

thread 2

​ bt打印子线程堆栈信息,发现阻塞在readHandler里的recv上。

如何处理?

​ 在之前的流程里,并没有很好的实现两个线程之间的业务分离,导致后面两个线程争抢数据。因此,我们彻底把两个线程的业务利用信号量来分离,主线程就负责和用户交互和发送数据给服务器,子线程就只接收服务器数据,然后处理,并且把处理结果发送给主线程,这样就实现了两个线程的分离。

​ 如图:

image-20231220111900861

​ 我们在主线程发送数据给服务器后就等待子线程的处理结果,

image-20231220111950335

​ 在子线程一直等待服务器发送数据,当接收到数据后就处理,然后把处理结果交给主线程的流程,这样就不会出现卡死的状态了。至此,问题解决。