概述
使用“带外数据”实现TCP心跳包
版权声明:本文为博主原创文章,未经博主允许不得转载。
公司有一个基于TCP的IM项目,开发人员将心跳包与数据流混在了一起,从而增加了数据提取的难度和出错的机率,我提出使用带外数据来实现心跳包,该开发人员认为这是一种过时的不被.NET支持的技术特性,其实.NET不是空中楼阁,他的所有技术都是基于原来WIN32技术的基础之上的,只不过增强了OO特性而已,为验证自己的想法,因此花费了几个小时编写了使用“带外数据”实现TCP心跳包的DEMO类,并成功通过测试。
该DEMO类分别对应SOCKET通讯的SERVER端和CLIENT端进行了不同处理,两端的心跳时间间隔最好设为一致,否则可能导致结果错误,此外,由于心跳包是定时异步执行的,在开发期间如果有暂停运行的调试需要,最好将心跳包功能暂时关闭,以免因心跳包发送或接收超时而抛出错误。
一、心跳包代码如下:
- '* *************************************************************** *
- '* 模块名称:NetHeartbeat.vb
- '* 功能描述:心跳包处理类
- '* 作者:lyserver
- '* 编码日期:2011年11月14日
- '* 修改日期:
- '* *************************************************************** *
- Imports System.Net.Sockets
- ''' <summary>
- ''' 心跳包处理类
- ''' </summary>
- Public Class NetHeartbeat
- Implements IDisposable
- ''' <summary>
- ''' 心跳包处理方式(发送或接收)
- ''' </summary>
- Public Enum MethodConstants
- [Send] = 0
- [Recv] = 1
- End Enum
- ''' <summary>
- ''' 心跳包描述类
- ''' </summary>
- Private Class HeartbeatInfo '心跳包信息
- Public Buffer As Byte() = New Byte(0) {} '心跳包数据缓冲区
- Public Times As Integer = 0 '心跳包检测次数
- Public Success As Boolean = True '心跳包发送或接收成功标志
- End Class
- Private m_Socket As Socket = Nothing '套接字
- Private WithEvents m_Timer As Timers.Timer = New Timers.Timer(1000) '定时器
- Private m_HeartbeatInfo As HeartbeatInfo = Nothing '心跳包描述对象
- Private m_Method As MethodConstants = MethodConstants.Send '心跳包处理方式
- Private m_TryTimes As UInteger = 5 '心跳包出错后的最大尝试次数
- Public Delegate Sub MyEventHandler(ByVal sender As NetHeartbeat, ByVal message As String)
- Public Event OnError As MyEventHandler
- Public Sub New()
- End Sub
- Public Sub New(ByVal sock As Socket)
- m_Socket = sock
- End Sub
- Public Sub New(ByVal sock As Socket, ByVal method As MethodConstants)
- m_Socket = sock
- m_Method = method
- End Sub
- Public Sub New(ByVal sock As Socket, ByVal method As MethodConstants, ByVal enabled As Boolean)
- m_Socket = sock
- m_Method = method
- Me.Enabled = enabled
- End Sub
- Public Sub New(ByVal sock As Socket, ByVal method As MethodConstants, ByVal enabled As Boolean, ByVal interval As Integer)
- m_Socket = sock
- m_Method = method
- Me.Enabled = enabled
- Me.Interval = interval
- End Sub
- ''' <summary>
- ''' 重置心跳包处理状态
- ''' </summary>
- Public Sub Reset()
- If m_HeartbeatInfo Is Nothing Then
- m_HeartbeatInfo = New HeartbeatInfo()
- End If
- m_HeartbeatInfo.Times = 0
- m_HeartbeatInfo.Success = True
- End Sub
- ''' <summary>
- ''' 绑定套接字,并指定心跳包处理方式
- ''' </summary>
- Public Sub Bind(ByVal sock As Socket, ByVal method As MethodConstants)
- m_Socket = sock
- m_Method = method
- End Sub
- ''' <summary>
- ''' 启动或停止心跳包
- ''' </summary>
- Public Property Enabled() As Boolean
- Get
- Return m_Timer.Enabled
- End Get
- Set(ByVal value As Boolean)
- Reset()
- m_Timer.Enabled = value
- End Set
- End Property
- ''' <summary>
- ''' 心跳包处理间隔时间(毫秒)
- ''' </summary>
- Public Property Interval() As UInteger
- Get
- Return m_Timer.Interval
- End Get
- Set(ByVal value As UInteger)
- m_Timer.Interval = value
- End Set
- End Property
- ''' <summary>
- ''' 心跳包接收或发送时出错、超时、失败后重试次数
- ''' </summary>
- Public Property TryTimes() As UInteger
- Get
- Return m_TryTimes
- End Get
- Set(ByVal value As UInteger)
- m_TryTimes = value
- End Set
- End Property
- ''' <summary>
- ''' 心跳包处理方式:接收或发送
- ''' </summary>
- Public Property Method() As MethodConstants
- Get
- Return m_Method
- End Get
- Set(ByVal value As MethodConstants)
- m_Method = value
- End Set
- End Property
- ''' <summary>
- ''' 发送心跳包
- ''' </summary>
- Private Sub SendHeartbeat()
- Try
- If m_HeartbeatInfo.Success = True Then '如果上一次发送动作已成功,则继续发送心跳包
- m_HeartbeatInfo.Success = False
- m_Socket.BeginSend(m_HeartbeatInfo.Buffer, 0, 1, SocketFlags.OutOfBand, AddressOf Me.EndSendOOB, Nothing)
- Else '否则,超时计数器加1
- m_TryTimes += 1
- End If
- Catch ex As Exception
- ErrorHandler(ex.Message)
- End Try
- End Sub
- Private Sub EndSendOOB(ByVal ar As IAsyncResult)
- Try
- Dim nSendCount As Integer = m_Socket.EndSend(ar)
- m_HeartbeatInfo.Success = True '设置心跳包发送成功标志
- m_TryTimes = 0 '超时计数器归0
- Catch ex As Exception
- ErrorHandler(ex.Message)
- End Try
- End Sub
- ''' <summary>
- ''' 接收心跳包
- ''' </summary>
- Private Sub RecvHeartbeat()
- Try
- If m_HeartbeatInfo.Success = True Then '如果上一次接收动作已成功,则继续接收心跳包
- m_HeartbeatInfo.Success = False
- m_Socket.BeginReceive(m_HeartbeatInfo.Buffer, 0, 1, SocketFlags.OutOfBand, AddressOf Me.EndRecvOOB, Nothing)
- Else '否则,超时计数器加1
- m_TryTimes += 1
- End If
- Catch ex As Exception
- ErrorHandler(ex.Message)
- End Try
- End Sub
- Private Sub EndRecvOOB(ByVal ar As IAsyncResult)
- Try
- Dim nRecvCount As Integer = m_Socket.EndReceive(ar)
- m_HeartbeatInfo.Success = True '设置心跳包接收成功标志
- m_TryTimes = 0 '超时计数器归0
- Catch ex As Exception
- ErrorHandler(ex.Message)
- End Try
- End Sub
- ''' <summary>
- ''' 定时检查心跳包发送或接收情况,并根据结果判断是否继续发送或接收心跳包,或者抛出心跳包处理异常
- ''' </summary>
- Private Sub m_Timer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles m_Timer.Elapsed
- If m_HeartbeatInfo Is Nothing Then
- m_Timer.Enabled = False
- Exit Sub
- End If
- Try
- If m_Method = MethodConstants.Send Then '如果心跳包处理方式为发送
- If m_HeartbeatInfo.Success = True Then '如果心跳包已发送成功,则继续发送
- SendHeartbeat()
- Console.WriteLine("心跳包发送成功")
- Else '否则,心跳包发送超时或出错
- If m_HeartbeatInfo.Times < m_TryTimes Then '如果超时或出错次数小于指定的次数,则继续发送
- SendHeartbeat()
- Else '否则,抛出心跳包发送异常错误
- ErrorHandler("心跳包发送超时")
- End If
- End If
- Else '如果心跳包处理方式为接收
- If m_HeartbeatInfo.Success = True Then '如果心跳包已接收成功,则继续接收
- RecvHeartbeat()
- Console.WriteLine("心跳包接收成功")
- Else '否则,心跳包接收超时或出错
- If m_HeartbeatInfo.Times < m_TryTimes Then '如果超时或出错次数小于指定的次数,则继续接收
- RecvHeartbeat()
- Else '否则,抛出心跳包接收异常错误
- ErrorHandler("心跳包接收超时")
- End If
- End If
- End If
- Catch ex As Exception
- ErrorHandler(ex.Message)
- End Try
- End Sub
- ''' <summary>
- ''' 异常处理
- ''' </summary>
- Private Sub ErrorHandler(ByVal message As String)
- Try
- m_Timer.Enabled = False
- If m_HeartbeatInfo IsNot Nothing Then
- m_HeartbeatInfo.Times = m_TryTimes
- m_HeartbeatInfo.Success = False
- End If
- Catch ex As Exception
- End Try
- RaiseEvent OnError(Me, message)
- End Sub
- Private disposedValue As Boolean = False ' 检测冗余的调用
- ' IDisposable
- Protected Overridable Sub Dispose(ByVal disposing As Boolean)
- If Not Me.disposedValue Then
- If disposing Then
- ' TODO: 显式调用时释放非托管资源
- End If
- ' TODO: 释放共享的非托管资源
- m_Timer.Enabled = False
- m_Socket = Nothing
- m_HeartbeatInfo = Nothing
- End If
- Me.disposedValue = True
- End Sub
- #Region " IDisposable Support "
- ' Visual Basic 添加此代码是为了正确实现可处置模式。
- Public Sub Dispose() Implements IDisposable.Dispose
- ' 不要更改此代码。请将清理代码放入上面的 Dispose(ByVal disposing As Boolean) 中。
- Dispose(True)
- GC.SuppressFinalize(Me)
- End Sub
- #End Region
- End Class
二、类使用说明:
1、如果要处理心跳包事件,请使用Private WithEvents m_Heartbeat As NetHeartbeat = New NetHeartbeat 来实例化心跳包。
2、如果要修改心跳包处理方式,请使用Heartbeat .Bind(sock,MethodConstants.Recv)方法来指定心跳包的处理套接字和处理方式,建议最好由客户端发送心跳包,服务器端接收心跳包。
3、如果要修改心跳包处理的时间间隔,请修改m_Heartbeat的Interval属性,该时间间隔的单位为毫秒。
4、如果要启动心跳包,请设置m_Heartbeat的Enabled属性为True。
最后
以上就是精明冰淇淋为你收集整理的使用“带外数据”实现TCP心跳包 使用“带外数据”实现TCP心跳包的全部内容,希望文章能够帮你解决使用“带外数据”实现TCP心跳包 使用“带外数据”实现TCP心跳包所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复