【问题标题】:How to close a TcpClient when the other end has been closed in .NET?当另一端在.NET中关闭时如何关闭TcpClient?
【发布时间】:2014-05-16 03:33:03
【问题描述】:

我有一个处理 TcpClients 的类。班级应该做的是:

    while the other end has not done a graceful close or we are stopping
    {
        receive a request
        process it
        send response
    }

由于我不知道其他客户端何时会发送请求,因此我无法在设置了超时的情况下进行读取,所以到目前为止我所拥有的是:

    While Not Me.Stopping()
        Try
            If tcpClient.Available >= My.Settings.minimumModBusTcpFrameSize Then
                processer = New MessageProcesser(Me, tcpClient)
                processer.ProcessMessage()
            End If
        Catch ex As TimeoutException
            ''#Do not nothing, the current message will timeout on origin too.
        End Try
    End While

这种方法的问题是我永远不知道客户端何时远程调用了 Close()。

有没有办法解决这个问题?

【问题讨论】:

    标签: .net tcpclient


    【解决方案1】:

    我不明白为什么您不能在超时的情况下执行Read...如果读取超时,您可以重试,而如果Read 返回 0,则连接已关闭。

    编辑:是的,我已经确认了这里的行为——进一步阅读确实似乎失败了。这真的很奇怪......我将我的答案留在这里,因为我觉得 应该 是合适的 - 希望在某个时候我有时间再次调查它。

    【讨论】:

    • 我发现 Read 和 IOException 而不是 TimeoutException 的问题。我只是在调查,并将返回结果。
    • 看起来当我超时时,TcpClient 已断开连接并且套接字已关闭,因此我无法再次重试读取。
    【解决方案2】:

    只是一个测试来显示实现 Jon Skeet 答案时发现的问题:

    Public Class Form1
    
    Private m_listener As Net.Sockets.TcpListener
    Private m_client As Net.Sockets.TcpClient
    Private m_stopping As Boolean
    
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim data As Byte()
        Dim dataLength As Integer
    
        ReDim data(512)
    
        m_listener = New Net.Sockets.TcpListener(Net.IPAddress.Any, 502)
        m_listener.Start()
        m_client = m_listener.AcceptTcpClient()
    
        m_client.GetStream().ReadTimeout = 1000
        m_client.GetStream().WriteTimeout = 1000
    
        While Not m_stopping
            Try
                dataLength = m_client.GetStream.Read(data, 0, data.Length)
                If dataLength = 0 Then
                    MsgBox("Disconnected")
                End If
            Catch ex As Exception When TypeOf (ex) Is TimeoutException OrElse (Not ex.InnerException Is Nothing AndAlso TypeOf (ex.InnerException) Is Net.Sockets.SocketException AndAlso DirectCast(ex.InnerException, Net.Sockets.SocketException).ErrorCode = 10060)
                ''# Just retry
            End Try
        End While
    End Sub
    
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        m_stopping = True
    End Sub
    End Class
    

    如果您使用 Telnet 客户端连接,您将收到 InvalidOperationException,因为套接字已在超时时间(一秒)后关闭。

    【讨论】:

      【解决方案3】:

      Jon 已经回答了,但是如果您可以控制客户端,那么您还可以添加一个表示“请关闭连接”的请求,这样您就可以尝试“优雅关闭”,并且只需使用 Jon 的“自动检测”如果客户端未能整齐地关闭连接,请处理。

      【讨论】:

        【解决方案4】:

        我有一个类似的案例,但使用的是 udpclient。我正在捕获一个 SocketException 以查明远程端点是否不再可用:

            While True
                Try
                    Dim RecievedBytes As Byte() = udp.Receive(mRemoteIP)
                    mMessage = System.Text.Encoding.ASCII.GetString(RecievedBytes)
                    RaiseEvent MessageRecieved()
                Catch ex As Sockets.SocketException
                    MsgBox("Your firewall may be blocking you from recieving messages")
                End Try
            End While
        

        【讨论】:

          【解决方案5】:

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-07-27
            • 1970-01-01
            • 2017-03-16
            相关资源
            最近更新 更多