您的位置:首页 > 理论基础 > 计算机网络

使用“带外数据”实现TCP心跳包

2016-03-16 17:19 507 查看


使用“带外数据”实现TCP心跳包

标签: 带外数据网络心跳包
2011-11-17 10:17 2398人阅读 评论(2) 收藏 举报


分类:

VB.NET(1)


版权声明:本文为博主原创文章,未经博主允许不得转载。

公司有一个基于TCP的IM项目,开发人员将心跳包与数据流混在了一起,从而增加了数据提取的难度和出错的机率,我提出使用带外数据来实现心跳包,该开发人员认为这是一种过时的不被.NET支持的技术特性,其实.NET不是空中楼阁,他的所有技术都是基于原来WIN32技术的基础之上的,只不过增强了OO特性而已,为验证自己的想法,因此花费了几个小时编写了使用“带外数据”实现TCP心跳包的DEMO类,并成功通过测试。

该DEMO类分别对应SOCKET通讯的SERVER端和CLIENT端进行了不同处理,两端的心跳时间间隔最好设为一致,否则可能导致结果错误,此外,由于心跳包是定时异步执行的,在开发期间如果有暂停运行的调试需要,最好将心跳包功能暂时关闭,以免因心跳包发送或接收超时而抛出错误。

一、心跳包代码如下:

[vb] view
plain copy

'* *************************************************************** *

'* 模块名称: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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: