基于QT开发多人聊天项目5:功能完善 多线程加锁以及网络的异常处理

本文介绍了在网络通信中如何处理服务器断开及网络异常情况,并通过心跳包机制保持连接稳定性。详细解析了客户端与服务器端的心跳包实现方式,并讨论了多线程模型中的加锁策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、服务器主动断开的处理

如果服务器断开连接,用于通信的套接字就会产生一个disconnected()信号:
//如果服务器断开连接,客户端要收到提示

connect(_socket, SIGNAL(disconnected()), this, SLOT(disconnected()));

在槽函数中,执行相关的处理函数。

2、网路异常处理

如果网络突然断开,服务器和客户端将断开连接。此时,服务器收不到客户端的发来的信息,但是却没有相关的处理操作。 客户端这面,由于在qt层面已经做好了封装,会产生disconnected信号,救会执行上面的:

connect(_socket, SIGNAL(disconnected()), this, SLOT(disconnected()));

为了解决这个问题,我们引入了心跳包的概念:客户端定时像服务器发送数据,如果客户端一段时间没有收到客户端的发来的心跳包或者是请求,就将这个客户端断开连接(关闭套接字)。
在这里,我们取巧(同样,也可以自己再定义一个数据包,用作心跳包),用客户端发送给服务器的用来请求在线列表的数据包作为心跳包:
.h:

	//用一个宏,定义最大允许收不到心跳包的次数
const int MAX_NO_HEART_TIMES = 5;
    //用来保存没有收到心跳包回应的次数
    int _noheartTimes;

cpp:

/*每发送一次请求在线列表的数据包,_noheartTimes就+1;当有收到心跳包回复的时候,就将这个值改成0, _noheartTimes = 0;*/
if (++ _noheartTimes >= MAX_NO_HEART_TIMES)
{
    //关闭套接字
    _socket->disconnectFromHost();
    disconnected();
}

至此,实现心跳包客户端功能!!
服务器端的心跳包机制实现:
用信号定时器alarm来实现:他的功能是,指定一个时间a(eg :1s),那么每隔1s,这个定时就会产生一个信号:SIGALARM。
但是这个信号定时器存在一个问题:
这个信号定时器只有一个,下一次使用,会将上一次的定时器覆盖掉。当同时又多个客户端在线的时候,会发生相互覆盖的问题。我们需要的是,能为每个客户端都提供一个定时器。
为了解决这个问题,我们使用:

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

这里,我们不是要用select()来实现多路IO转接,只是单纯的利用的他的超时时间进行定时。一个反应堆中,只有一个套接字(对应一个客户端),这样就实现了给每个客户端分配一个客户端
注:为什么用select(),不用poll或epoll()
因为当我们的文件描述符很大的时候(eg:1000)poll()依然会从头开始查找(轮询),哪个文件描述符是就绪的状态。效率很低!!!虽然poll也需要轮询,但是select他所监听的最大文件描述符是1024(缺点变优点)。而poll则没有限制!!
而用epoll,为了一个文件描述符,维护一棵树,有点“大材小用”。所以,我们使用select.
那么,我们在什么地方封装select()呢?
如果网络断开,服务器会阻塞在read()。因为,我们在将read()进行封装,封装select()进去,让他变成一个具有超时的read()。

3、多线程模型加锁

不允许同时操作的地方:对链表进行读写得时候。
那么,我们使用什么锁呢?是互斥锁耗时读写锁????
因为除了客户端登录和退出得时候,我们是添加和删除锁,其他时候,都是在对链表进行读操作。因此,我们使用读写锁!!

		//定义一个读写锁
		static pthread_rwlock_t rwlock;

		//在对链表进行插入的时候,使用写锁
		//写锁定
        pthread_rwlock_wrlock(&rwlock);
        insertlist(head, clientfd);
        // 解 写锁定
        pthread_rwlock_unlock(&rwlock);

		//在对链表进行比哪里得时候,使用读锁
        //读锁定
        pthread_rwlock_rdlock(&rwlock);
        index += getlist(head, send_buf + index, send_len - index);
        // 解 读锁定
        pthread_rwlock_unlock(&rwlock);

		//在对链表进行删除得时候,使用写锁
        //写锁定
        pthread_rwlock_wrlock(&rwlock);
        deletelist(head, clntfd);
        // 解 写锁定
        pthread_rwlock_unlock(&rwlock);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

One Piece&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值