2019独角兽企业重金招聘Python工程师标准>>>

  假设一种情景: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等技术。

转载于:https://my.oschina.net/u/3386278/blog/2875983

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

  1. ASM心跳超时检测之--Delayed ASM PST heart beats

    近日,连续收到ASM磁盘dismount,并且是错误"Waited 15 secs for write IO to PST"的问题,这是ASM特有的心跳超时检测,ASM insta ...

  2. 记录一次net.ipv4.tcp_tw_recycl快速回收机制导致的tcp连接失败问题

    背景: 公司业务为长连接业务,然后部分用户反馈至客服为连接失败(登录),通过分析日志和抓包看是有请求包到达服务端的,但是没有回包,然后联系云厂商查看后台针对cvm的实例监控,发现有部分客户端的syn包 ...

  3. Socket编程之一个端口能建立多个TCP连接?

    个人博客:www.saoguang.top 一.背景 记得上学期暑假的时候我基于MFC写了一个简单的聊天程序.那个聊天程序,两部分组成,监听客户端请求线程和客户端请求处理线程. 1.服务器接收到登陆请 ...

  4. 【Ping检测】使用Ping命令检查网络连接情况

    ▚ 01 Ping检测 ping是诊断网络问题的一个常用命令行工具. 1.1 ping 127.0.0.1 127.0.0.1是本机循环地址,若是无法ping通,则表示本机的TCP/IP协议无法正常工 ...

  5. c语言 socket 断开自动连接,如何优雅地断开TCP连接?

    socket关闭: close()和shutdown()的差异 对于一个tcp连接,在c语言里一般有2种方法可以将其关闭: close(sock_fd); 或者 shutdown(sock_fd, . ...

  6. socket心跳检测

    一.什么是心跳检测 判断对方(设备,进程或其它网元)是否正常动行,一般采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经当掉.用于检测TCP的异常断开. 基本原因是服务器端不能 ...

  7. socket心跳机制图片_WebSocket心跳检测和重连机制

    1. 心跳重连原由 心跳和重连的目的用一句话概括就是客户端和服务端保证彼此还活着,避免丢包发生. websocket连接断开有以下两种情况: 前端断开 在使用websocket过程中,可能会出现网络断 ...

  8. 【Java 网络编程】客户端 Socket 配置 ( 超时时间 | 端口复用 | Nagle 算法 | 心跳包机制 | 连接关闭机制 | 缓冲区大小 | 性能权重设置 | 紧急数据设置 )

    文章目录 I 设置读取超时时间 II Socket 复用绑定端口设置 III 开启 Nagle 算法 ( 沾包 ) IV 心跳包机制 V 连接关闭处理 VI Socket 紧急数据内敛设置 VII S ...

  9. Socket心跳包异常检测的C语言实现,服务器与客户端代码案例

    在Socket心跳机制中,心跳包可以由服务器发送给客户端,也可以由客户端发送给服务器,不过比较起来,前者开销可能较大.本文实现的是由客户端给服务器发送心跳包,服务器不必返回应答包,而是通过判断客户在线 ...

最新文章

  1. pythonl学习笔记——爬虫的基本常识
  2. 中国大学MOOC 人工智能导论第五章测试
  3. Google Pixel 2(XL)录像画质提升背后
  4. 内存管理简介之Buddy算法和slab分配
  5. 使用DBCA创建数据库
  6. 解读NoSQL最新现状和趋势:云NoSQL数据库将成重要增长引擎
  7. Oracle view 小结片段
  8. html语言中base,HTML base 标签
  9. excel清空sheet内容 poi_java – 从POI中的WorkBook中删除工作表
  10. css设置background图片的位置实现居中
  11. hive 操作(三)——hive 的数据模型
  12. opencv4快速入门pdf_云复工提升工作效率之九 福昕PDF阅读器
  13. NYOJ-88-汉诺塔(一)
  14. c语言中字母与allse,几读音是什么
  15. java念整数 你的程序要读入一个整数,范围是[-100000,100000]。然后,用汉语拼音将这个整数的每一位输出出来。 如输入1234,则输出: yi er san si
  16. Centos系统下搭建Smokeping 方法
  17. HM编码器代码阅读(30)——帧间预测之AMVP模式(五)运动估计
  18. 步进电机原理与驱动方式
  19. moveit perception
  20. C语言求解三个数的中间值

热门文章

  1. PAT 乙级 1009. 说反话 (20) Java版
  2. 蓝桥杯 ALGO-111 算法训练 明明的随机数
  3. L1-026. I Love GPLT-PAT团体程序设计天梯赛GPLT
  4. [置顶] 使用Joson的格式字符串在Socket中通讯时数据格式的转换
  5. 2018百度原创力排行榜公示(转载)
  6. Android Jni开发,报com.android.ide.common.process.ProcessException: Error configuring 错误解决方案...
  7. Android SDK 开发——发布使用踩坑之路
  8. 初学react.js
  9. kuangbin专题一 简单搜索
  10. JFreechart 在linux下不显示及中文乱码问题