lyserver的专栏

───永远的码农

使用“带外数据”实现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。

阅读更多
个人分类: VB.NET
想对作者说点什么? 我来说一句

ATFView2014查看转换工具 by SunnyBoxs

2014年03月01日 447KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭