C++聊天集群服务器解决客户端注销登录问题
客户端如何处理注销登录问题?
问题描述:
在客户端登录后进行注销选择,然后重新登录刚才注销的账号,直接卡死。注意这是概率发生,因为是主线程和子线程抢服务器发送的信息,只有子线程抢到才会发生卡死
问题产生原因分析:
前置条件:主线程循环等待用户输入选择(第一张图是死循环,send后立马recv),子线程阻塞等待服务器数据。
在客户端注销账号之前,我们启动了一个子线程来充当接收线程,阻塞等待服务器数据。当客户端注销账号后,并没有立马关闭子线程,然后立马重新登录,这时主线程会组装一个json数据发给服务器,服务器处理完后发送处理结果回客户端,此时当子线程竞争过主线程,抢到recv接收数据,但是子线程并没有处理相应的措施,因此一次循环后马上又阻塞在recv函数,而主线程因为一直未收到数据,因仍然阻塞在recv函数,所以造成了客户端死机的现象。
排查过程:
使用命令
gdb attach pid(阻塞的进程id号)
进入调试状态,然后
info threads
查看客户端的两个线程信息,发现两个线程都阻塞在recv函数上然后使用命令
bt
打印线程调用堆栈,发现主线程阻塞在send函数后紧接的recv函数上,切换线程
thread 2
bt打印子线程堆栈信息,发现阻塞在readHandler里的recv上。
如何处理?
在之前的流程里,并没有很好的实现两个线程之间的业务分离,导致后面两个线程争抢数据。因此,我们彻底把两个线程的业务利用信号量来分离,主线程就负责和用户交互和发送数据给服务器,子线程就只接收服务器数据,然后处理,并且把处理结果发送给主线程,这样就实现了两个线程的分离。
如图:
我们在主线程发送数据给服务器后就等待子线程的处理结果,
在子线程一直等待服务器发送数据,当接收到数据后就处理,然后把处理结果交给主线程的流程,这样就不会出现卡死的状态了。至此,问题解决。