Question

I have a class that handles TcpClients. What the class should do is:

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

As I don't know when the other client will send a request I can not do a Read with a timeout set, so what I have until now is this:

    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

The problem with this approach is that I never know when a client has done a remote call to Close().

Is there a way of solving this problem?

Was it helpful?

OTHER TIPS

I don't see why you can't do a Read with a timeout... If the read times out you could retry it, whereas if Read returns 0 then the connection has been closed.

EDIT: Yup, I've confirmed the behaviour here - a further read does indeed appear to fail. That's really strange... I'm leaving my answer here as the one I feel should be appropriate - hopefully at some point I'll have time to investigate it again.

Just a test to show the problem found implementing the Jon Skeet answer:

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

If you connect with a Telnet client you will get an InvalidOperationException because the socket has been closed after the timeout time (one second).

Jon's already answered, but if you have control of the client, then you could also add a request that means "please close the connection", so you can attempt a "graceful close", and just use Jon's "auto detect" approach if the client fails to close the connection tidily.

I have a similar case but with udpclient. I am catching a SocketException to find out if the remote endpoint is no longer available:

    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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top