views:

181

answers:

2

Hello,

I have a listener that returns a TcpClient that must read data until the other side closes or until we stop.

I have created a simple Form with two buttons. One starts one connection and the other stops it.

The problem is that when I have a IOException due to the timeout of the Read the socket is closed and I cannot communicate with it again. I cannot reconnect it as is the other side the one to establish the connection.

Just execute the code and telnet to the port 502, wait one second and you will see the problem.

How would you do that?

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")
            Else
                MsgBox(dataLength.ToString() & " bytes received")
            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

This question is related to the question: http://stackoverflow.com/questions/1973956/how-to-close-a-tcpclient-when-the-other-end-has-been-closed-in-net

FINALLY: The real problem is that to know when a client has closed the connection I must do a Read and check for a return of 0. If the Read has infinite Timeout the call will block and I will not be able to end the application until the Read returns something, if the read has a finite Timeout I can have a TimeoutException and then the Socket will be closed. So ... HOW THE HELL I'M SUPPOSED TO KNOW WHEN THE OTHER PART HAS CLOSED THE CONNETION? I can not believe that something so "easy" is so hard.

A: 

Rather than trying to Read from the stream and catch a TimeoutException, you could check the Available property to know whether there is data to be read. That way the TimeoutException won't happen, and you will still be able to reuse the TcpClient

Thomas Levesque
If I do that I will never know when the remote host closes the connection because to do that I need to do a Read and it will return 0.
SoMoS
You shouldn't rely on the result of Read to detect wether the connection is closed... you should use the Connected property instead
Thomas Levesque
That's not true. The Connected property is only updated after a Read or a Write so if I never do a Read the Connected will remain True forever.
SoMoS
+1  A: 

I've found the solution. What has to be done is to do a Read() with timeout set to infinite and when you need to unblock that Read call you just need to call to Close() and the Read() will unblock.

SoMoS
fantastic, this is finally the solution that worked for me, too. I'm glad you posted it!
Dave