Search code examples
vb.nettcpstreamtcpclienttcplistener

Tcp Connection only reads one message


I am trying to create a simple Game which needs a TcpConnection.

So I created a Server class which wraps the TcpListener, same goes with Client and TcpClient. But when I attach my Listener Sub and send Messages it only gets called on the first one:

Server

Imports System.Net.Sockets

Public Class Server
    Inherits ConnectionPartner

    Private Server As TcpListener
    Private Client As TcpClient
    Private Stream As NetworkStream
    Private Port As Integer

    Public Sub Init(ByVal port As Integer)
        Server = New TcpListener(port)
    End Sub

    Public Sub Open()
        Server.Start()
        Client = Server.AcceptTcpClient()
        Stream = Client.GetStream()
    End Sub

    Public Overrides Sub Write(ByVal Message As String)
        Dim Bytes() As Byte = Encoder.GetBytes(Message)
        Me.Write(Bytes)
    End Sub
    Public Overrides Sub Write(ByVal Message() As Byte)
        If Stream Is Nothing Then Return
        Stream.Write(Message, 0, Message.Length)
    End Sub

    Public Overrides Sub Listen(ByVal Handler As Action(Of String))
        Listen(Sub(ByVal Message() As Byte)
                   Handler(Encoder.GetString(Message))
               End Sub)
    End Sub
    Public Overrides Sub Listen(ByRef Handler As Action(Of Byte()))
        Dim Buffer(Client.ReceiveBufferSize()) As Byte

        While (True)
            Utils.Log("Listening")
            Client.GetStream().Read(Buffer, 0, Buffer.Length)
            Handler(Buffer)
        End While
    End Sub

    Public Sub Close()
        Try
            Client.Close()
        Catch ex As Exception

        End Try
        Try
            Server.Stop()
        Catch ex As Exception

        End Try
    End Sub

End Class

Client

Imports System.Net
Imports System.Net.Sockets

Public Class Client
    Inherits ConnectionPartner

    Private Socket As TcpClient
    Private Stream As NetworkStream
    Private Port As Integer

    Public Sub Init()
        Socket = New TcpClient()
    End Sub

    Public Sub Open(ByVal Ip As IPAddress, ByVal Port As Integer)
        Socket.Connect(Ip, Port)
        Stream = Socket.GetStream()
    End Sub

    Public Overrides Sub Write(ByVal Message As String)
        Dim Bytes() As Byte = Encoder.GetBytes(Message)
        Me.Write(Bytes)
    End Sub
    Public Overrides Sub Write(ByVal Message() As Byte)
        If Stream Is Nothing Then Return
        Stream.Write(Message, 0, Message.Length)
    End Sub

    Public Overrides Sub Listen(ByVal Handler As Action(Of String))
        Listen(Sub(ByVal Message() As Byte)
                   Handler(Encoder.GetString(Message))
               End Sub)
    End Sub
    Public Overrides Sub Listen(ByRef Handler As Action(Of Byte()))
        Dim Buffer(Socket.ReceiveBufferSize()) As Byte

        While (True)
            Utils.Log("Listening")
            Stream.Read(Buffer, 0, Buffer.Length)
            Handler(Buffer)
        End While
    End Sub

    Public Sub Close()
        Try
            Socket.Close()
        Catch ex As Exception

        End Try
    End Sub

End Class

My Test Class

Imports System.Net
Imports System.Threading

Class MainWindow

    Private Server As Server = New Server()
    Private Client As Client = New Client()
    Private C As Connection
    Private Port As Integer = My.Resources.Port

    Private Sub button_client_init_Click(sender As Object, e As RoutedEventArgs) Handles button_client_init.Click
        Client.Init()
    End Sub

    Private Sub button_client_start_Click(sender As Object, e As RoutedEventArgs) Handles button_client_start.Click
        Client.Open(Dns.GetHostEntry("localhost").AddressList(1), Port)
    End Sub

    Private Sub button_client_write_Click(sender As Object, e As RoutedEventArgs) Handles button_client_write.Click
        Client.Write("bar")
    End Sub

    Private Sub button_client_listen_Click(sender As Object, e As RoutedEventArgs) Handles button_client_listen.Click
        Dim T As Thread = New Thread(Sub()
                                         Client.Listen(Sub(ByVal M As String)
                                                           Log("Client:" & M)
                                                       End Sub)
                                     End Sub)
        T.Start()
    End Sub

    Private Sub button_server_init_Click(sender As Object, e As RoutedEventArgs) Handles button_server_init.Click
        Server.Init(Port)
    End Sub

    Private Sub button_server_start_Click(sender As Object, e As RoutedEventArgs) Handles button_server_start.Click
        Dim T As Thread = New Thread(Sub()
                                         Server.Open()
                                     End Sub)
        T.Start()
    End Sub

    Private Sub button_server_write_Click(sender As Object, e As RoutedEventArgs) Handles button_server_write.Click
        Server.Write("foo")
    End Sub

    Private Sub button_server_listen_Click(sender As Object, e As RoutedEventArgs) Handles button_server_listen.Click
        Dim T As Thread = New Thread(Sub()
                                         Server.Listen(Sub(ByVal M As String)
                                                           Log("Server: " & M)
                                                       End Sub)
                                     End Sub)
        T.Start()
    End Sub

End Class

Edit:

I tried to do the whole thing with StreamReader/Writers. Now I am not even getting a síngle Message:

Server

Imports System.IO
Imports System.Net.Sockets

Public Class Server
    Inherits ConnectionPartner

    Private Server As TcpListener
    Private Client As TcpClient
    Private Reader As StreamReader
    Private Writer As StreamWriter
    Private Port As Integer

    Public Sub Init(ByVal port As Integer)
        Server = New TcpListener(port)
    End Sub

    Public Sub Open()
        Server.Start()
        Client = Server.AcceptTcpClient()
        Dim Stream As NetworkStream = Client.GetStream()
        Reader = New StreamReader(Stream)
        Writer = New StreamWriter(Stream)
    End Sub

    Public Overrides Sub Write(ByVal Message As String)
        Writer.WriteLine(Message)
    End Sub
    Public Overrides Sub Write(ByVal Message() As Byte)
    End Sub

    Public Overrides Sub Listen(ByVal Handler As Action(Of String))
        While True
            Dim Message As String = Reader.ReadLine()
            Handler(Message)
        End While
    End Sub
    Public Overrides Sub Listen(ByRef Handler As Action(Of Byte()))
    End Sub

    Public Sub Close()
        Try
            Client.Close()
        Catch ex As Exception

        End Try
        Try
            Server.Stop()
        Catch ex As Exception

        End Try
    End Sub


End Class

Client

Imports System.IO
Imports System.Net
Imports System.Net.Sockets

Public Class Client
    Inherits ConnectionPartner

    Private Socket As TcpClient
    Private Reader As StreamReader
    Private Writer As StreamWriter
    Private Port As Integer

    Public Sub Init()
        Socket = New TcpClient()
    End Sub

    Public Sub Open(ByVal Ip As IPAddress, ByVal Port As Integer)
        Socket.Connect(Ip, Port)
        Dim Stream As NetworkStream = Socket.GetStream()
        Reader = New StreamReader(Stream)
        Writer = New StreamWriter(Stream)
    End Sub

    Public Overrides Sub Write(ByVal Message As String)
        Writer.WriteLine(Message)
    End Sub
    Public Overrides Sub Write(ByVal Message() As Byte)
    End Sub

    Public Overrides Sub Listen(ByVal Handler As Action(Of String))
        While True
            Dim Message As String = Reader.ReadLine()
            Handler(Message)
        End While
    End Sub
    Public Overrides Sub Listen(ByRef Handler As Action(Of Byte()))
    End Sub

    Public Sub Close()
        Try
            Socket.Close()
        Catch ex As Exception

        End Try
    End Sub


End Class

Solution:

Forgot to flush the Messages.

Server

Imports System.IO
Imports System.Net
Imports System.Net.Sockets

Public Class Server
    Inherits ConnectionPartner

    Private Server As TcpListener
    Private Client As TcpClient
    Private Reader As StreamReader
    Private Writer As StreamWriter
    Private Port As Integer

    Public Sub Init(ByVal Ip As IPAddress, ByVal Port As Integer)
        Server = New TcpListener(Ip, Port)
    End Sub

    Public Sub Open()
        Server.Start()
        Client = Server.AcceptTcpClient()
        Dim Stream As NetworkStream = Client.GetStream()
        Reader = New StreamReader(Stream)
        Writer = New StreamWriter(Stream)
        Writer.AutoFlush = True
    End Sub

    Public Overrides Sub Start(ByVal Ip As IPAddress, ByVal Port As Integer)
        Me.Init(Ip, Port)
        Me.Open()
    End Sub

    Public Overrides Sub Write(ByVal Message As String)
        Writer.WriteLine(Message)
    End Sub

    Public Overrides Sub Listen(ByVal Handler As Action(Of String))
        While True
            Dim Message As String = Reader.ReadLine()
            Handler(Message)
        End While
    End Sub

    Public Overrides Sub Close()
        Try
            Client.Close()
        Catch ex As Exception

        End Try
        Try
            Server.Stop()
        Catch ex As Exception

        End Try
    End Sub


End Class

Client

Imports System.IO
Imports System.Net
Imports System.Net.Sockets

Public Class Client
    Inherits ConnectionPartner

    Private Socket As TcpClient
    Private Reader As StreamReader
    Private Writer As StreamWriter
    Private Port As Integer

    Public Sub Init()
        Socket = New TcpClient()
    End Sub

    Public Sub Open(ByVal Ip As IPAddress, ByVal Port As Integer)
        Socket.Connect(Ip, Port)
        Dim Stream As NetworkStream = Socket.GetStream()
        Reader = New StreamReader(Stream)
        Writer = New StreamWriter(Stream)
        Writer.AutoFlush = True
    End Sub

    Public Overrides Sub Start(ByVal Ip As IPAddress, ByVal Port As Integer)
        Me.Init()
        Me.Open(Ip, Port)
    End Sub

    Public Overrides Sub Write(ByVal Message As String)
        Writer.WriteLine(Message)
    End Sub

    Public Overrides Sub Listen(ByVal Handler As Action(Of String))
        While True
            Dim Message As String = Reader.ReadLine()
            Handler(Message)
        End While
    End Sub

    Public Overrides Sub Close()
        Try
            Socket.Close()
        Catch ex As Exception

        End Try
    End Sub


End Class

Solution

  • TCP does not provide messages at all. It provides a boundaryless stream of bytes. When you Read you can get back any number of bytes as low as one. You code needs to assume arbitrary chunking. Here, you are assuming that you get a full buffer each time.

    You can use BinaryReader.ReadBytes to read an exact number of bytes. StreamReader/Writer makes text-based protocols much easier.

    ReceiveBufferSize is not the number of bytes incoming. It's value is meaningless, don't look at it.