扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
怎么在c#中利用socket实现一个心跳超时检测的功能?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
站在用户的角度思考问题,与客户深入沟通,找到老边网站设计与老边网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:网站设计、成都网站制作、企业官网、英文网站、手机端网站、网站推广、域名申请、雅安服务器托管、企业邮箱。业务覆盖老边地区。假设socket3有新的数据到达,需要更新socket3所在的时间轴,处理逻辑如下:
基本的处理思路就是增加时间轴概念。将socket按最后更新时间排序。因为时间是连续的,不可能将时间分割太细。首先将时间离散,比如属于同一秒内的更新,被认为是属于同一个时间点。离散的时间间隔称为时间刻度,该刻度值可以根据具体情况调整。刻度值越小,超时计算越精确;但是计算量增大。如果时间刻度为10毫秒,则一秒的时间长度被划分为100份。所以需要对更新时间做规整,代码如下:
DateTime CreateNow() { DateTime now = DateTime.Now; int m = 0; if(now.Millisecond != 0) { if(_minimumScaleOfMillisecond == 1000) { now = now.AddSeconds(1); //尾数加1,确保超时值大于 给定的值 } else { //如果now.Millisecond为16毫秒,精确度为10毫秒。则转换后为20毫秒 m = now.Millisecond - now.Millisecond % _minimumScaleOfMillisecond + _minimumScaleOfMillisecond; if(m>=1000) { m -= 1000; now = now.AddSeconds(1); } } } return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second,m); }
属于同一个时间刻度的socket,被放入在一个哈希表中(见图中Group)。存放socket的类如下:
class SameTimeKeyGroup{ DateTime _timeStamp; public DateTime TimeStamp => _timeStamp; public SameTimeKeyGroup(DateTime time) { _timeStamp = time; } public HashSet KeyGroup { get; set; } = new HashSet (); public bool ContainKey(T key) { return KeyGroup.Contains(key); } internal void AddKey(T key) { KeyGroup.Add(key); } internal bool RemoveKey(T key) { return KeyGroup.Remove(key); } }
定义一个List表示时间轴:
List> _listTimeScale = new List >();
在_listTimeScale 前端的时间较旧,所以链表前端就是有可能超时的socket。
当有socket需要更新时,需要快速知道socket所在的group。这样才能将socket从旧的group移走,再添加到新的group中。需要新增一个链表:
Dictionary> _socketToSameTimeKeyGroup = new Dictionary >();
查找socket的上一个群组。如果该群组对应的时刻和当前时刻相同(时间都已经离散,才有可能相同),无需更新时间轴。
从旧的群组删除,增加到新的群组。
public void UpdateTime(T key) { DateTime now = CreateNow(); //是否已存在,从上一个时间群组删除 if (_socketToSameTimeKeyGroup.ContainsKey(key)) { SameTimeKeyGroupgroup = _socketToSameTimeKeyGroup[key]; if (group.ContainKey(key)) { if (group.TimeStamp == now) //同一时间更新,无需移动 { return; } else { group.RemoveKey(key); _socketToSameTimeKeyGroup.Remove(key); } } } //从超时组 删除 _timeoutSocketGroup.Remove(key); //加入到新组 SameTimeKeyGroup groupFromScaleList = GetOrCreateSocketGroup(now, out bool newCreate); groupFromScaleList.AddKey(key); _socketToSameTimeKeyGroup.Add(key, groupFromScaleList); if (newCreate) { AdjustTimeout(); } }
时间轴从旧到新,对比群组的时间与超时时刻。就是链表_listTimeScale,从0开始查找。
//////timeLimit 值为超时时刻限制 ///比如DateTime.Now.AddMilliseconds(-1000);表示 返回一秒钟以前的数据 /// /// 该时间以前的socket会被返回 ///public List GetTimeoutValue(DateTime timeLimit, bool remove = true) { if((DateTime.Now - timeLimit) > _maxSpan ) { Debug.Write("GetTimeoutSocket timeLimit 参数有误!"); } //从超时组 读取 List result = new List (); foreach(T key in _timeoutSocketGroup) { _timeoutSocketGroup.Add(key); } if(remove) { _timeoutSocketGroup.Clear(); } while (_listTimeScale.Count > 0) { //时间轴从旧到新,查找对比 SameTimeKeyGroup group = _listTimeScale[0]; if(timeLimit >= group.TimeStamp) { foreach (T key in group.KeyGroup) { result.Add(key); if (remove) { _socketToSameTimeKeyGroup.Remove(key); } } if(remove) { _listTimeScale.RemoveAt(0); } } else { break; } } return result; }
//创建变量。较大超时时间为600秒,时间刻度为1秒 TimeSpanManage_deviceActiveManage = TimeSpanManage .Create(TimeSpan.FromSeconds(600), 1000); //当有数据到达时,调用更新函数 _deviceActiveManage.UpdateTime(socket); //需要在线程或定时器中,每隔一段时间调用,找出超时的socket //找出超时时间超过600秒的socket。 foreach (Socket socket in _deviceActiveManage.GetTimeoutValue(DateTime.Now.AddSeconds(-600))) { socket.Close(); }
////// 超时时间 时间间隔处理 /// class TimeSpanManage{ TimeSpan _maxSpan; int _minimumScaleOfMillisecond; int _scaleCount; List > _listTimeScale = new List >(); private TimeSpanManage() { } /// /// /// /// 较大时间时间 /// 最小刻度(毫秒) ///public static TimeSpanManage Create(TimeSpan maxSpan, int minimumScaleOfMillisecond) { if (minimumScaleOfMillisecond <= 0) throw new Exception("minimumScaleOfMillisecond 小于0"); if (minimumScaleOfMillisecond > 1000) throw new Exception("minimumScaleOfMillisecond 不能大于1000"); if (maxSpan.TotalMilliseconds <= 0) throw new Exception("maxSpan.TotalMilliseconds 小于0"); TimeSpanManage result = new TimeSpanManage (); result._maxSpan = maxSpan; result._minimumScaleOfMillisecond = minimumScaleOfMillisecond; result._scaleCount = (int)(maxSpan.TotalMilliseconds / minimumScaleOfMillisecond); result._scaleCount++; return result; } Dictionary > _socketToSameTimeKeyGroup = new Dictionary >(); public void UpdateTime(T key) { DateTime now = CreateNow(); //是否已存在,从上一个时间群组删除 if (_socketToSameTimeKeyGroup.ContainsKey(key)) { SameTimeKeyGroup group = _socketToSameTimeKeyGroup[key]; if (group.ContainKey(key)) { if (group.TimeStamp == now) //同一时间更新,无需移动 { return; } else { group.RemoveKey(key); _socketToSameTimeKeyGroup.Remove(key); } } } //从超时组 删除 _timeoutSocketGroup.Remove(key); //加入到新组 SameTimeKeyGroup groupFromScaleList = GetOrCreateSocketGroup(now, out bool newCreate); groupFromScaleList.AddKey(key); _socketToSameTimeKeyGroup.Add(key, groupFromScaleList); if (newCreate) { AdjustTimeout(); } } public bool RemoveSocket(T key) { bool result = false; if (_socketToSameTimeKeyGroup.ContainsKey(key)) { SameTimeKeyGroup group = _socketToSameTimeKeyGroup[key]; result = group.RemoveKey(key); _socketToSameTimeKeyGroup.Remove(key); } //从超时组 删除 bool result2 = _timeoutSocketGroup.Remove(key); return result || result2; } /// ///timeLimit 值为超时时刻限制 ///比如DateTime.Now.AddMilliseconds(-1000);表示 返回一秒钟以前的数据 /// /// 该时间以前的socket会被返回 ///public List GetTimeoutValue(DateTime timeLimit, bool remove = true) { if((DateTime.Now - timeLimit) > _maxSpan ) { Debug.Write("GetTimeoutSocket timeLimit 参数有误!"); } //从超时组 读取 List result = new List (); foreach(T key in _timeoutSocketGroup) { _timeoutSocketGroup.Add(key); } if(remove) { _timeoutSocketGroup.Clear(); } while (_listTimeScale.Count > 0) { //时间轴从旧到新,查找对比 SameTimeKeyGroup group = _listTimeScale[0]; if(timeLimit >= group.TimeStamp) { foreach (T key in group.KeyGroup) { result.Add(key); if (remove) { _socketToSameTimeKeyGroup.Remove(key); } } if(remove) { _listTimeScale.RemoveAt(0); } } else { break; } } return result; } HashSet _timeoutSocketGroup = new HashSet (); private void AdjustTimeout() { while (_listTimeScale.Count > _scaleCount) { SameTimeKeyGroup group = _listTimeScale[0]; foreach (T key in group.KeyGroup) { _timeoutSocketGroup.Add(key); } _listTimeScale.RemoveAt(0); } } private SameTimeKeyGroup GetOrCreateSocketGroup(DateTime now, out bool newCreate) { if (_listTimeScale.Count == 0) { newCreate = true; SameTimeKeyGroup result = new SameTimeKeyGroup (now); _listTimeScale.Add(result); return result; } else { SameTimeKeyGroup lastGroup = _listTimeScale[_listTimeScale.Count - 1]; if (lastGroup.TimeStamp == now) { newCreate = false; return lastGroup; } newCreate = true; SameTimeKeyGroup result = new SameTimeKeyGroup (now); _listTimeScale.Add(result); return result; } } DateTime CreateNow() { DateTime now = DateTime.Now; int m = 0; if(now.Millisecond != 0) { if(_minimumScaleOfMillisecond == 1000) { now = now.AddSeconds(1); //尾数加1,确保超时值大于 给定的值 } else { //如果now.Millisecond为16毫秒,精确度为10毫秒。则转换后为20毫秒 m = now.Millisecond - now.Millisecond % _minimumScaleOfMillisecond + _minimumScaleOfMillisecond; if(m>=1000) { m -= 1000; now = now.AddSeconds(1); } } } return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second,m); } } class SameTimeKeyGroup { DateTime _timeStamp; public DateTime TimeStamp => _timeStamp; public SameTimeKeyGroup(DateTime time) { _timeStamp = time; } public HashSet KeyGroup { get; set; } = new HashSet (); public bool ContainKey(T key) { return KeyGroup.Contains(key); } internal void AddKey(T key) { KeyGroup.Add(key); } internal bool RemoveKey(T key) { return KeyGroup.Remove(key); } }
看完上述内容,你们掌握怎么在c#中利用socket实现一个心跳超时检测的功能的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读!
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流