文档章节

socket心跳超时检测,快速处理新思路(适用于超大量TCP连接情况下)

SEOwhywhy
 SEOwhywhy
发布于 2018/11/14 12:06
字数 1244
阅读 2
收藏 0

  假设一种情景:TCP服务器有1万个客户端连接,如果客户端5秒钟不发数据,则要断开。服务端如何检测客户端是否超时?这看起来是一个非常简单的问题,其实不然!
  
  最简单的处理方法是:启动一个线程,每隔一段时间,检查每个连接是否超时。每次处理需要1万次检查。计算量太大!检查的时间间隔不能太小,否则大大增加计算量;如果间隔时间太大,超时误差会增大。
  
  本文提出一种新颖的处理方法,就是针对这个看似简单而不易解决的问题!(以下用socket表示一个客户端连接)
  
  1 内存布局图
  
  假设socket3有新的数据到达,需要更新socket3所在的时间轴,处理逻辑如下:
  
  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(www.dasheng178.com/);
  
  }
  
  }
  
  复制代码
  
  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 www.mcyllpt.com- timeLimit) www.ysyl157.com > _maxSpan )
  
  {
  
  Debug.Write("GetTimeoutSocket timeLimit 参数有误!");
  
  }
  
  //从超时组 读取
  
  List<T> result = new List<www.huachengj1980.com>();
  
  foreach(www.dfgjyl.cn T www.furong157.com key in _timeoutSocketGroup)
  
  {
  
  _timeoutSocketGroup.Add(key);
  
  }
  
  if(remove)
  
  {
  
  _timeoutSocketGroup.Clear(www.yongxinzaixian.cn);
  
  }
  
  while (_listTimeScale.Count www.mengzhidu178.com> 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 完整代码
  
  View Code
  
  专注.NET、VC++。擅长WPF、WinForm、Socket等技术。

© 著作权归作者所有

SEOwhywhy
粉丝 8
博文 135
码字总数 296494
作品 0
私信 提问
socket心跳超时检测,快速处理新思路(适用于超大量TCP连接情况下)

假设一种情景:TCP服务器有1万个客户端连接,如果客户端5秒钟不发数据,则要断开。服务端如何检测客户端是否超时?这看起来是一个非常简单的问题,其实不然! 最简单的处理方法是:启动一个线...

源之缘
2018/11/14
0
0
关于TCP长连接和发送心跳的一些理解

作者:郭无心 链接:https://www.zhihu.com/question/35013918/answer/63664974 来源:知乎 著作权归作者所有,转载请联系作者获得授权。 游戏服务器常常有心跳包的设计。 我们的心跳包就是为...

huang19830104
2018/06/28
0
0
rabbitmq——heartbeat

heartbeat通常用来检测通信的对端是否存活(未正常关闭socket连接而异常crash)。其基本原理是检测对应的socket连接上数据的收发是否正常,如果一段时间内没有收发数据,则向对端发送一个心跳...

hncscwc
2014/01/23
0
9
长连接和端连接和心跳包机制

TCP连接简介 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接, 当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接, 连接的建立是需要...

nardoo
2018/03/09
0
0
从零开始学netty——心跳检测

通过学习前面的内容,大家基本可以使用netty来开发程序了,本文再补充一下心跳检测部分,属于是完善功能的内容了。 为什么要有心跳检测 大部分人听到心跳连接这个名词都是在数据库连接池上,...

xpbob
2018/05/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Linux使用源码包安装软件

前言: 最近整理一些以前的学习笔记。 过去都是存储在本地,此次传到网络留待备用。 源码包 Linux软件多数免费、开源,是开发人员编写的,具有很强可读性的一组相关代码文本。 源码包 --> 编...

迷失De挣扎
今天
2
0
IPv4如何转换为IPv6?

ipv6已经逐渐在应用,现在已经有很多的运营商支持ipv6,前天我们也发布了如何让电脑使用ipv6地址?有很多朋友在问?ipv6有什么作用,它的表示方式是什么,今天我们来一起来详细了解下ipv6相关计...

xiangyunyan
今天
3
0
小白讲网络安全系列

注入攻击防护 XSS注入 SQL注入 命令注入 文件上传 文件解压缩 CSRF防护 对称加密 非对称加密 数字证书 数字签名 完整性校验 消息验证码 单向散列Hash函数 口令单向加密算法 审计日志 认证鉴权...

一刀
今天
2
0
MYSQL 嵌套事务(SAVEPOINT) 与Spring 事务传播

摘要 savepoint 关键字可以实现嵌套事务。结合savepoint关键字,更方便理解spring的事务传播。 事务嵌套 初始化表脚本 drop table t;create table t(a int, primary key(a)); 开启事务 my...

liangxiao
今天
4
0
Chrome OS 更新新版本可让Linux访问USB连接的Android设备

谷歌再次为Chrome OS带来了重大版本更新,使版本号达到了75。本次更新的一大亮点就是允许在Chrome OS上运行的Linux能够识别通过USB方式连接的Android设备,能够让用户使用Linux进行调试等等。...

linuxCool
昨天
17
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部