【问题标题】:How to handle TCPListener "An existing connection was forcibly closed by the remote host"如何处理 TCPListener “现有连接被远程主机强行关闭”
【发布时间】:2011-10-20 23:58:14
【问题描述】:

我有一个使用 TCPListener 和异步方法 BeginAcceptTCPClient 的 tcp 服务器:

 Imports System.Net.Sockets
Imports System.Threading
Imports System.Net

Public Class TCPServer
    Private mPort As Integer
    Public Event IncomingMessage(ByVal Message As String, ByVal IP As String)
    'This signals threadpool threads to stop...
    Private mStopServer As ManualResetEvent
    Private mListener As TcpListener
    Public Sub New(ByVal Port As Integer)
        mPort = Port
        Start()

    End Sub

    Public Sub Start()
        Try
            If mListener Is Nothing Then
                mListener = New TcpListener(IPAddress.Any, mPort)
            End If

            mListener.Start()
            AcceptClients()
            mStopServer = New ManualResetEvent(False)
        Catch ex As Exception
            ExceptionHandling.LogError(ex)
        End Try

    End Sub

    Private Sub AcceptClients()
        Try
            Dim result As IAsyncResult = mListener.BeginAcceptTcpClient(AddressOf HandleAsyncConnection, mListener)
        Catch ex As Exception
            ExceptionHandling.LogError(ex)
        End Try

    End Sub

    Dim so As New Object
    Public Sub StopListening()
        Try
            mStopServer.Set()
            mListener.Stop()
        Catch ex As Exception
            ExceptionHandling.LogError(ex)
        End Try

    End Sub


    Private Sub HandleAsyncConnection(ByVal result As IAsyncResult)
        Try


            If Not mStopServer.WaitOne(0) Then
                Dim listener As TcpListener = DirectCast(result.AsyncState, TcpListener)
                If listener Is Nothing Then Exit Sub
                Dim client As TcpClient = listener.EndAcceptTcpClient(result)
                Trace.WriteLine("Connected to new client")
                ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ProcessClient), client)
                AcceptClients()

            End If
        Catch ex As Exception
            ExceptionHandling.LogError(ex)

        End Try
    End Sub



    Private Sub ProcessClient(ByVal client As Object)

        Dim newClient As TcpClient = DirectCast(client, TcpClient)


        Try
            ' Buffer for reading data
            Dim bytes As Byte() = New Byte(32 * 1024) {}
            Dim clientData As New System.Text.StringBuilder()


            Using ns As NetworkStream = newClient.GetStream()
                ' set initial read timeout to 1 minute to allow for connection
                ns.ReadTimeout = 60000
                ' Loop to receive all the data sent by the client.
                Dim bytesRead As Integer = 0
                Do
                    ' read the data
                    Try
                        If mStopServer.WaitOne(0) Then Exit Sub

                        bytesRead = ns.Read(bytes, 0, bytes.Length)
                        If bytesRead > 0 Then
                            clientData.Append(System.Text.Encoding.UTF8.GetString(bytes, 0, bytesRead))
                            ' decrease read timeout to 1 second now that data is coming in
                            ns.ReadTimeout = 1000
                        End If

                    Catch ioe As IO.IOException
                        Trace.WriteLine(ioe.ToString)
                        bytesRead = 0
                        Dim bError() As Byte = Error400()
                        ns.Write(bError, 0, bError.Length)

                    End Try

                Loop While ns.DataAvailable
                ForwardData(clientData.ToString, newClient.Client.RemoteEndPoint.ToString)
                'Trace.Write(clientData.ToString())
                'Acknowledge success
                bytes = Ack200()
                ns.Write(bytes, 0, bytes.Length)
            End Using

        Catch ex As Exception
            ExceptionHandling.LogError(ex)

        Finally
            ' stop talking to client
            If newClient IsNot Nothing Then
                newClient.Close()
            End If
        End Try
    End Sub

    Private Sub ForwardData(ByVal Data As String, ByVal IP As String)
        RaiseEvent IncomingMessage(Data, IP)
    End Sub
    Public Function Ack200() As Byte()
        Return System.Text.Encoding.UTF8.GetBytes("Okay")
    End Function

    Public Function Error400() As Byte()
        Return System.Text.Encoding.UTF8.GetBytes("Error")
    End Function

End Class

我的问题是,我很少在 HandleAsyncConnection 方法中遇到异常: EndAcceptTCPClient 方法中的“现有连接被远程主机强制关闭”。此时,TCPListener 似乎停止了监听。问题是我不能轻易测试它,因为它只发生在远程测试虚拟机上,并且每 24 小时左右才发生一次。如果我知道如何使用测试客户端重新创建错误,我将能够解决这个问题。 Wireshark 显示在异常发生时正在发送的重置 [RST] 数据包。因此,我要么需要知道如何处理异常,要么需要知道如何使用测试客户端重新创建问题。

【问题讨论】:

    标签: vb.net tcp tcplistener


    【解决方案1】:

    您可以通过重新启动服务器或退回您连接的服务来重现问题。基本上任何杀死你正在与之通信的进程的东西都应该产生这个错误。

    要处理,您几乎只需捕获异常并尝试重新连接到服务器,可能必须等到它再次可用。

    编辑: 您的代码停止侦听,因为您调用 AcceptClients 是在异常发生之后,因此您永远不会回到 BeginAcceptTCPClient 函数。另外,我不确定这如何与 Async 调用一起使用,但对我来说它看起来像是一个无限递归。 BeginAcceptTCP 客户端调用HandlAsynConnection 的委托,后者调用AcceptClients,从而开始循环。在我看来,经过这么多连接后,您应该会收到堆栈溢出错误。您可以在 HandleAsyncConnection 中的 try 块中添加 finally 子句并在那里调用它,因此在发生错误时总是会调用它。

        Try
    
    
            If Not mStopServer.WaitOne(0) Then
                Dim listener As TcpListener = DirectCast(result.AsyncState, TcpListener)
                If listener Is Nothing Then Exit Sub
                Dim client As TcpClient = listener.EndAcceptTcpClient(result)
                Trace.WriteLine("Connected to new client")
                ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ProcessClient), client)
    
            End If
        Catch ex As Exception
            ExceptionHandling.LogError(ex)
        Finally            
            AcceptClients()
        End Try
    

    【讨论】:

    • 我尝试在连接过程中杀死我的测试客户端,但在这种方法中它通常不会给我这个异常。相反,它会在 ProcessClient 中生成异常,并且服务器会继续接受客户端。另外,如何重新连接服务器或重新启动侦听器侦听?怎么知道什么时候可用。我第一次尝试重新启动服务器导致端口已在使用中的异常。
    • 我想现在在代码中看到为什么服务器在错误后停止接受,我更新了我的答案。
    • 是的,这是有效的。它经历了几次这样的例外。非常感谢!
    猜你喜欢
    • 2021-05-12
    相关资源
    最近更新 更多