温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么在c#中利用socket实现一个心跳超时检测的功能

发布时间:2021-03-09 16:00:28 来源:亿速云 阅读:724 作者:Leah 栏目:开发技术

怎么在c#中利用socket实现一个心跳超时检测的功能?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

1 内存布局图

怎么在c#中利用socket实现一个心跳超时检测的功能

假设socket3有新的数据到达,需要更新socket3所在的时间轴,处理逻辑如下:

怎么在c#中利用socket实现一个心跳超时检测的功能

2 处理过程分析:

基本的处理思路就是增加时间轴概念。将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>>();

2.1 当socket有新的数据到达时,处理步骤:

  • 查找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();
  }
 }

2.2 获取超时的socket

 时间轴从旧到新,对比群组的时间与超时时刻。就是链表_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;
 }

3 使用举例

//创建变量。最大超时时间为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();
}

4 完整代码

/// <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实现一个心跳超时检测的功能的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI