Search code examples
.netvb.nettcpclienttcplistener

.NET TcpClient/TcpListener only receiving first message


I have a simple program which sends/receives messages using TCP. For some reason, it receives the first message fine, but unless I close and reconnect with the client, the server doesn't receive any messages beyond the first from that client. I am providing a stripped down version of the code below (so please excuse the lack of exception handling).

Private TCPWriter As TcpClient
Private TCPListener As TcpListener

Private Sub Send(sender As Object, e As RoutedEventArgs)
    Connect()
    WriteTextToServer(MessageBox.Text)
End Sub

Private Sub Connect()
    If TCPWriter Is Nothing Then
        TCPWriter = New TcpClient
    End If

    If Not TCPWriter.Connected Then
        TCPWriter.Connect(System.Net.IPAddress.Parse(IPBox.Text), PortBox.Text)
        TCPWriter.NoDelay = True
    End If
End Sub

Private Sub WriteTextToServer(inMsg As String)
    Dim data() As Byte = Encoding.ASCII.GetBytes(inMsg + vbCrLf)
    Dim ns As NetworkStream = TCPWriter.GetStream()

    If ns.CanWrite Then
        ns.Write(inMsg, 0, inMsg.Length - 1)
    End If
End Sub

Private Sub ReadNewDataFromClient(ByVal inStatus As IAsyncResult)
    Dim clientSocket As TcpClient
    Dim datalen As Integer
    Dim buf() As Byte
    Dim message As String

    clientSocket = CType(inStatus.AsyncState, TcpListener).EndAcceptTcpClient(inStatus)
    clientSocket.ReceiveTimeout = 5000

    datalen = clientSocket.Available
    If datalen > 0 Then
        'get all data at once ...
        buf = New Byte(datalen - 1) {}
        clientSocket.GetStream().Read(buf, 0, buf.Length)

        message = Encoding.ASCII.GetString(buf, 0, buf.Count - 2)
        Me.Dispatcher.Invoke(Sub() ReadNewDataFromClients.Add(ReadNewDataFromClients.Count.ToString + ": " + message))
    End If

    TCPListener.BeginAcceptTcpClient(New AsyncCallback(AddressOf ReadNewDataFromClient), Nothing)
End Sub

If I change the Connect() method to the below code, it will work but doesn't seem like the correct way to go about it.

Private Sub Connect()
    If TCPWriter IsNot Nothing Then
        TCPWriter.close
        TCPWriter = nothing
        Threading.Thread.Sleep(1000)
    End If

    TCPWriter = New TcpClient

    If Not TCPWriter.Connected Then
        TCPWriter.Connect(System.Net.IPAddress.Parse(IPBox.Text), PortBox.Text)
        TCPWriter.NoDelay = True
    End If
End Sub

What am I doing wrong?


Solution

  • You're using BeginAcceptTcpClient to receive the next "message", this is used to accept a new connection (which is why it works when you disconnect/reconnect for each message). You should just use Read/BeginRead to read the next message.

    This being said, you also need to understand that TCP is a stream protocol and as such, it has no concept of "messages", and perhaps most importantly, there is no reliable correspondence between the number of calls to Write made by the sender and the number of calls to Read required by the receiver to read the data sent.

    If you want to use TCP for discrete messages, you need to send the data such that you can split it back up into the individual messages on the receiving end. This is usually done by means of "framing" the messages by prefixing them with a length indicator of a fixed size (e.g. 4 bytes).

    You also need to ensure that you observe the return value of the Read method, which will indicate how many bytes were actually read into the buffer.