说起网络应用编程,想到最多的就是聊天类的软件。当然,在这类软件中,一般都会有一个用户掉线检测功能。今天我们就通过使用自定义的HeartBeat方式来检测用户的掉线情况。
心跳包实现思路
我们采用的思路是:客户端连接上服务端以后,服务端维护一个在线用户字典,客户端每隔一段时间,向服务器发送一个心跳包,服务器接收到包以后,字典数据的值都会更新为0;一旦服务端超过规定时间没有接收到客户端发来的包,字典数据将会递增加一,当字典数据的值累计大于等于三,则视为掉线。
代码逻辑
客户端每隔一段时间,发送一个心跳包:
#region 心跳Timer计数事件 private void heartbeatTimer_Tick(object sender, EventArgs e) { currentCount++; if (currentCount == heartbeatCount) { txtMessage.Append("开始发送心跳包"); MessageEntity entity = new MessageEntity(); entity.MessageType = MessagePicks.Heartbeat; entity.NickName = loginName; WriteToStream(entity); currentCount = 0; } } #endregion
在服务端,会开启一个定时器,定时将userOnLineCounter中的值递增加一。如果此时收到客户端的心跳包,则将userOnLineCounter中的值重置。
private void heartbeatTimer_Tick(object sender, EventArgs e) { tickCountInStep++; if (tickCountInStep == tickCount) { if (userCollection.Count > 0) { //计数器自动递增 expiryCountInStep++; foreach (User user in userLists) { userOnLineCounter[user]++; } //连续监测三次之后,开始监测集合中的掉线情况 if (expiryCountInStep == expiryCount) { //寻找集合中“掉线”的用户 var disconnectedUsers = userOnLineCounter.Where(p => p.Value >= 3).ToList(); foreach (var disconnectedUser in disconnectedUsers) { txtLog.Append("用户" + disconnectedUser.Key.name + "掉线!"); //删除集合中被视为掉线的用户 userLists.Remove(disconnectedUser.Key); userOnLineCounter.Remove(disconnectedUser.Key); //开始广播发送掉线用户 MessageEntity entity = new MessageEntity(); entity.MessageType = MessagePicks.OffLine; EndPoint curOfflineUserEP = disconnectedUser.Key.client.Client.RemoteEndPoint; string userName = disconnectedUser.Key.name; entity.MessageContentEx.Add(curOfflineUserEP, userName); ObjectInversion inversion = new ObjectInversion(); byte[] byteArr = inversion.SerializeTo((object)entity); try { foreach (User user in userLists) { user.writer.Write(byteArr); user.writer.Flush(); } } catch { } } expiryCountInStep = 0; } } tickCountInStep = 0; } } }
收到客户端心跳包,自动重置计数器。
case MessagePicks.Heartbeat: txtLog.Append("收到客户端" + entity.NickName + "的心跳回应包."); if (userOnLineCounter.ContainsKey(user)) userOnLineCounter[user] = 0; else userOnLineCounter.Add(user, 0); break;
效果图
(图1:三个客户端连接一个服务器)
(图2:用户“上善若水”掉线)
(图3:用户“古道热肠”掉线)
程序暂时还未完全完成,有需要的可以参考下。当然也期待大家的各种思路。
代码很丑,期望大家指点下重构的方法。
源码下载
=====================2014年9月24日重构版本=======================
用户实体内部通过维护一个timer计数器,实现心跳检测,心跳超时功能。