TCP连接有效性检测方法

      在写TCP服务的时候经常需要面对的问题就是如何知道一个TCP连接当前是否有效,但这个问题对很多初入门的同学来说是很困惑的,主要原因是当对方关闭连接后,另一方无法有效的知道;对于同步操作来说可以通过设置操作超时来解决,但异步操作则没有这样方便的了,那只能等keepalive的检测完成引发异步回调了。      

      那在编写应用的时候一般通讯什么方式来检测连接的有效性呢?解决方法一般有两种一种是设置TCP的keepalive时间,另一种则是通过Ping,Pong的方式来实现。前者相对比较简单通过socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null)方法设置即可,以下主要但要通过Ping,Pong的方式来实现应用层面的TCP连接有效性检测。通过Ping,Pong来处理有两种方式:服务器主动和被动。

主动

这种方式主要是由服务器发起,然后由客户端响应;服务检测每个连接Pong响应情况,如果连接在一段时间内没有Pong回应则把相应连接关闭并处理相关会话资源。

被动

这种方式由Client发起Ping然后由服务端回应Pong,如果Client是同步操作的话其实服务端是不需要应答Pong包。服务端检测每个连接最近的Ping时间,如果超过一段时间没有Ping的情况把相应连接关闭并处理相关会话资源。


模式选择

从上面的两种方式来看显然是被动模式更节省服务器资源,如果采用主动的话服务器还必须启用一个定时器对现有在线接进行发送Ping操作;被动模式就完全不需要了,只有接收到客户端Ping回应一个Pong操作。

检测算法

一般情况会用一个定时器隔一段时间对所有Client检测一次,看对应的Ping时间是否超时,如果是则直接关闭和释放资源。但这样是要对所有连接进行扫描,其实在应用中只有很小部分连接是无效的,如果针对所有在线连接进行一个扫描那的确一个比较花成本的工作。为了解决全扫描的情况,可以采用一种简单的算法LRU,通过LRU算法在检测的时候只要扫冷区数据即可,这样就可以达到只扫描Ping超时的连接。LRU具体处理结构如下:

 

以下给出相关LRU实现的c#版本代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/// <summary>
    /// 基于LRU算法的连接检测
    /// </summary>
    public class LRUDetect:IDisposable
    {
          
        /// <summary>
        /// 构建检测器
        /// </summary>
        /// <param name="timeout">超时时间以毫秒为单位</param>
        public LRUDetect(int timeout)
        {
            mTimeout = timeout;
            mTimer = new System.Threading.Timer(OnDetect, null, mTimeout, mTimeout);
        }      
  
        private int mTimeout;
  
        private System.Threading.Timer mTimer;
  
        private LinkedList<Node> mLinkedList = new LinkedList<Node>();
  
        /// <summary>
        /// 更新连接
        /// </summary>
        /// <param name="connection">连接信息</param>
        public void Update(IConnecton connection)
        {
            lock (this)
            {
                LinkedListNode<LRUDetect.Node> node = connection.Node;
                if (node != null)
                {
                    node.Value.LastActiveTime = Environment.TickCount;
                    mLinkedList.Remove(node);
                    mLinkedList.AddFirst(node);
                }
                else
                {
  
                    node = mLinkedList.AddFirst(new Node());
                    node.Value.LastActiveTime = Environment.TickCount;
                    node.Value.Connection = connection;
                    connection.Node = node;
                }
            }
        }
  
        /// <summary>
        /// 删除连接
        /// </summary>
        /// <param name="connection">连接信息</param>
        public void Delete(IConnecton connection)
        {
            lock (this)
            {
                LinkedListNode<LRUDetect.Node> node = connection.Node;
                if (node != null)
                {
                    node.Value.Connection = null;
                    mLinkedList.Remove(node);
                }
            }
        }
  
        private void OnDetect(object state)
        {
            lock (this)
            {
                int cutime = Environment.TickCount;
                LinkedListNode<Node> last = mLinkedList.Last;
                while (last !=null && last.Value.Detect(cutime,mTimeout))
                {
                    last.Value.Connection.TimeOut();
                    last.Value.Connection = null;
                    mLinkedList.RemoveLast();
                    last = mLinkedList.Last;
                }
            }
        }
  
        /// <summary>
        /// 连接描述接口
        /// </summary>
        public interface IConnecton
        {
            /// <summary>
            /// 获取对应在LRU算法中的节点
            /// </summary>
            LinkedListNode<LRUDetect.Node> Node
            {
                get;
                set;
            }
            /// <summary>
            /// 超时操作,当LRU算法检测到应该连接超时的时候会调用该方法
            /// </summary>
            void TimeOut();
        }
  
        /// <summary>
        /// 节点信息
        /// </summary>
        public class Node
        {
            /// <summary>
            /// 最后活动时间
            /// </summary>
            public int LastActiveTime;
            /// <summary>
            /// 相关连接信息
            /// </summary>
            public IConnecton Connection;
  
            /// <summary>
            /// 检测是否过期
            /// </summary>
            /// <param name="cutime"></param>
            /// <param name="timeout"></param>
            /// <returns></returns>
            public bool Detect(int cutime,int timeout)
            {
                return Math.Abs(cutime - LastActiveTime) > timeout;
            }
        }
  
        /// <summary>
        /// 释放对象
        /// </summary>
        public void Dispose()
        {
            if (mTimer != null)
                mTimer.Dispose();
        }
    }
posted @   beetlex  阅读(11327)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
历史上的今天:
2012-03-12 int 转byte[]的相关方法和应用场景
点击右上角即可分享
微信分享提示