您好,登錄后才能下訂單哦!
怎么在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<T> { DateTime _timeStamp; public DateTime TimeStamp => _timeStamp; public SameTimeKeyGroup(DateTime time) { _timeStamp = time; } public HashSet<T> KeyGroup { get; set; } = new HashSet<T>(); 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<SameTimeKeyGroup<T>> _listTimeScale = new List<SameTimeKeyGroup<T>>();
在_listTimeScale 前端的時間較舊,所以鏈表前端就是有可能超時的socket。
當有socket需要更新時,需要快速知道socket所在的group。這樣才能將socket從舊的group移走,再添加到新的group中。需要新增一個鏈表:
Dictionary<T, SameTimeKeyGroup<T>> _socketToSameTimeKeyGroup = new Dictionary<T, SameTimeKeyGroup<T>>();
查找socket的上一個群組。如果該群組對應的時刻和當前時刻相同(時間都已經離散,才有可能相同),無需更新時間軸。
從舊的群組刪除,增加到新的群組。
public void UpdateTime(T key) { DateTime now = CreateNow(); //是否已存在,從上一個時間群組刪除 if (_socketToSameTimeKeyGroup.ContainsKey(key)) { SameTimeKeyGroup<T> group = _socketToSameTimeKeyGroup[key]; if (group.ContainKey(key)) { if (group.TimeStamp == now) //同一時間更新,無需移動 { return; } else { group.RemoveKey(key); _socketToSameTimeKeyGroup.Remove(key); } } } //從超時組 刪除 _timeoutSocketGroup.Remove(key); //加入到新組 SameTimeKeyGroup<T> groupFromScaleList = GetOrCreateSocketGroup(now, out bool newCreate); groupFromScaleList.AddKey(key); _socketToSameTimeKeyGroup.Add(key, groupFromScaleList); if (newCreate) { AdjustTimeout(); } }
時間軸從舊到新,對比群組的時間與超時時刻。就是鏈表_listTimeScale,從0開始查找。
/// <summary> ///timeLimit 值為超時時刻限制 ///比如DateTime.Now.AddMilliseconds(-1000);表示 返回一秒鐘以前的數據 /// </summary> /// <param name="timeLimit">該時間以前的socket會被返回</param> /// <returns></returns> public List<T> GetTimeoutValue(DateTime timeLimit, bool remove = true) { if((DateTime.Now - timeLimit) > _maxSpan ) { Debug.Write("GetTimeoutSocket timeLimit 參數有誤!"); } //從超時組 讀取 List<T> result = new List<T>(); foreach(T key in _timeoutSocketGroup) { _timeoutSocketGroup.Add(key); } if(remove) { _timeoutSocketGroup.Clear(); } while (_listTimeScale.Count > 0) { //時間軸從舊到新,查找對比 SameTimeKeyGroup<T> 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<Socket> _deviceActiveManage = TimeSpanManage<Socket>.Create(TimeSpan.FromSeconds(600), 1000); //當有數據到達時,調用更新函數 _deviceActiveManage.UpdateTime(socket); //需要在線程或定時器中,每隔一段時間調用,找出超時的socket //找出超時時間超過600秒的socket。 foreach (Socket socket in _deviceActiveManage.GetTimeoutValue(DateTime.Now.AddSeconds(-600))) { socket.Close(); }
/// <summary> /// 超時時間 時間間隔處理 /// </summary> class TimeSpanManage<T> { TimeSpan _maxSpan; int _minimumScaleOfMillisecond; int _scaleCount; List<SameTimeKeyGroup<T>> _listTimeScale = new List<SameTimeKeyGroup<T>>(); private TimeSpanManage() { } /// <summary> /// /// </summary> /// <param name="maxSpan">最大時間時間</param> /// <param name="minimumScaleOfMillisecond">最小刻度(毫秒)</param> /// <returns></returns> public static TimeSpanManage<T> 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<T> result = new TimeSpanManage<T>(); result._maxSpan = maxSpan; result._minimumScaleOfMillisecond = minimumScaleOfMillisecond; result._scaleCount = (int)(maxSpan.TotalMilliseconds / minimumScaleOfMillisecond); result._scaleCount++; return result; } Dictionary<T, SameTimeKeyGroup<T>> _socketToSameTimeKeyGroup = new Dictionary<T, SameTimeKeyGroup<T>>(); public void UpdateTime(T key) { DateTime now = CreateNow(); //是否已存在,從上一個時間群組刪除 if (_socketToSameTimeKeyGroup.ContainsKey(key)) { SameTimeKeyGroup<T> group = _socketToSameTimeKeyGroup[key]; if (group.ContainKey(key)) { if (group.TimeStamp == now) //同一時間更新,無需移動 { return; } else { group.RemoveKey(key); _socketToSameTimeKeyGroup.Remove(key); } } } //從超時組 刪除 _timeoutSocketGroup.Remove(key); //加入到新組 SameTimeKeyGroup<T> 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<T> group = _socketToSameTimeKeyGroup[key]; result = group.RemoveKey(key); _socketToSameTimeKeyGroup.Remove(key); } //從超時組 刪除 bool result2 = _timeoutSocketGroup.Remove(key); return result || result2; } /// <summary> ///timeLimit 值為超時時刻限制 ///比如DateTime.Now.AddMilliseconds(-1000);表示 返回一秒鐘以前的數據 /// </summary> /// <param name="timeLimit">該時間以前的socket會被返回</param> /// <returns></returns> public List<T> GetTimeoutValue(DateTime timeLimit, bool remove = true) { if((DateTime.Now - timeLimit) > _maxSpan ) { Debug.Write("GetTimeoutSocket timeLimit 參數有誤!"); } //從超時組 讀取 List<T> result = new List<T>(); foreach(T key in _timeoutSocketGroup) { _timeoutSocketGroup.Add(key); } if(remove) { _timeoutSocketGroup.Clear(); } while (_listTimeScale.Count > 0) { //時間軸從舊到新,查找對比 SameTimeKeyGroup<T> 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<T> _timeoutSocketGroup = new HashSet<T>(); private void AdjustTimeout() { while (_listTimeScale.Count > _scaleCount) { SameTimeKeyGroup<T> group = _listTimeScale[0]; foreach (T key in group.KeyGroup) { _timeoutSocketGroup.Add(key); } _listTimeScale.RemoveAt(0); } } private SameTimeKeyGroup<T> GetOrCreateSocketGroup(DateTime now, out bool newCreate) { if (_listTimeScale.Count == 0) { newCreate = true; SameTimeKeyGroup<T> result = new SameTimeKeyGroup<T>(now); _listTimeScale.Add(result); return result; } else { SameTimeKeyGroup<T> lastGroup = _listTimeScale[_listTimeScale.Count - 1]; if (lastGroup.TimeStamp == now) { newCreate = false; return lastGroup; } newCreate = true; SameTimeKeyGroup<T> result = new SameTimeKeyGroup<T>(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<T> { DateTime _timeStamp; public DateTime TimeStamp => _timeStamp; public SameTimeKeyGroup(DateTime time) { _timeStamp = time; } public HashSet<T> KeyGroup { get; set; } = new HashSet<T>(); 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實現一個心跳超時檢測的功能的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。