locked
.Net non-blocking TcpClient RRS feed

  • Question

  • I have working example of TCP listener which receives data on particular PORT and if needed sends response. The problem is that when script sends response whole process is waiting and doesn't accept new connections. Maybe someone can help to improve code so it start accepting new connections without blocking?

    Public Shared Sub test()
            Dim listener As TcpListener = Nothing
            Dim port As Integer = 555
    
            Try
                listener = New TcpListener(IPAddress.Any, port)
                listener.Start()
    
                'Buffer for reading data
                Dim bytes(1024) As Byte
                Dim data As String = Nothing
    
                ' Enter the listening loop.
                While True
                    Console.Write("Waiting for a connection... ")
                    ' Perform a blocking call to accept requests.
                    ' You could also use server.AcceptSocket() here.
                    Dim client As TcpClient = listener.AcceptTcpClient()
                    Console.WriteLine("Connected!")
    
                    data = Nothing
    
                    ' Get a stream object for reading and writing
                    Dim stream As NetworkStream = client.GetStream()
    
                    ' Loop to receive all the data sent by the client.
                    Dim i As Integer = stream.Read(bytes, 0, bytes.Length)
    
                    While (i <> 0)
    
                        ' Translate data bytes to a ASCII string.
                        data = Encoding.ASCII.GetString(bytes, 0, i)
                        Console.WriteLine("Received: {0}", data)
    
                        If data.Contains("ppp") Then
                            Console.WriteLine("Contains ppp")
                            ' Send back a response.
                            Response(stream, "my_response")
                        End If
    
                        i = stream.Read(bytes, 0, bytes.Length)
                    End While
                    ' Shutdown and end connection
                    Console.WriteLine("connection closed")
                End While
            Catch e As Exception
                Console.WriteLine(e)
            Finally
    
                If listener IsNot Nothing Then
                    listener.Stop()
                End If
            End Try
    
        End Sub
    
        Public Shared Sub Response(stream As Object, message As String)
            Dim response As Byte() = Encoding.ASCII.GetBytes(message)
            stream.Write(response, 0, response.Length)
            Console.WriteLine("response {0} sent", message)
        End Sub

    Thursday, May 16, 2019 7:05 AM

All replies

  • Hi,
    you can use BeginAcceptTcpClient instead of AcceptTcpClient. Every Accept (in AsyncCallback) will be executed in a separate thread.

    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Thursday, May 16, 2019 7:34 AM
  • I made it using BeginAcceptTcpClient. But could you please show how to make my example asynchronous using AcceptTcpClientAsync?

    • Edited by lifeisshort Friday, May 17, 2019 9:36 AM mistype
    Friday, May 17, 2019 9:36 AM
  • Hi,
    try this demo:

    Imports System.IO
    Imports System.Net
    Imports System.Net.Sockets
    Imports System.Text
    Imports System.Threading
    
    Module Module1
      Sub Main()
        Try
          Dim server As New DemoServer
          Task.Run(New Action(AddressOf server.Start))
          Threading.Thread.Sleep(500)
          Dim client As New DemoClient
          Task.Run(New Action(AddressOf client.Send))
          Task.Run(New Action(AddressOf client.Send))
          Task.Run(New Action(AddressOf client.Send))
        Catch ex As Exception
          Console.WriteLine(ex.ToString)
        End Try
        Console.WriteLine("Continue enter key")
        Console.ReadKey()
      End Sub
    
      Public Class DemoClient
    
        Public Sub Send()
          Dim host() As Byte = New Byte() {127, 0, 0, 1}
          Dim port As Integer = 555
          Dim client As New TcpClient()
          client.BeginConnect(New IPAddress(host), port, AddressOf DoSend, client)
        End Sub
    
        Private Sub DoSend(ar As IAsyncResult)
          Dim data = Encoding.ASCII.GetBytes($"ppp")
          Dim client = TryCast(ar.AsyncState, TcpClient)
          Using stream As NetworkStream = client.GetStream()
            stream.Write(data, 0, data.Length)
          End Using
        End Sub
    
      End Class
    
      Public Class DemoServer
    
        Private listener As TcpListener = Nothing
        Private port As Integer = 555
        ' Thread signal.
        Private tcpClientConnected As New ManualResetEvent(False)
    
        Friend Sub Start()
          Dim ipAddre = IPAddress.Loopback
          listener = New TcpListener(IPAddress.Any, port)
          listener.Start()
    
          Console.WriteLine("Server is running")
          Console.WriteLine($"Server listening on port {port}")
          ' async wait for client
          Try
            Do
              ' Set the event to nonsignaled state.
              tcpClientConnected.Reset()
              Console.WriteLine("Server waiting for connections...")
              listener.BeginAcceptTcpClient(AddressOf DoAccept, listener)
              ' Wait until a connection Is made And processed before continuing.
              tcpClientConnected.WaitOne()
            Loop
          Catch ex As Exception
            Console.WriteLine(ex.Message)
          End Try
        End Sub
    
        Private Sub DoAccept(ar As IAsyncResult)
          'Buffer for reading data
          Dim bytes(1023) As Byte
          Dim sb As New StringBuilder()
          ' detect server object
          Dim listener = TryCast(ar.AsyncState, TcpListener)
          ' detect TcpClient
          Dim client = listener.EndAcceptTcpClient(ar)
          ' Signal the calling thread to continue.
          tcpClientConnected.Set()
          ' Get a stream object for reading and writing
          Using stream As NetworkStream = client.GetStream()
            ' Loop to receive all the data sent by the client.
            Dim i As Integer = stream.Read(bytes, 0, bytes.Length)
            While i <> 0
              ' Translate data bytes to a ASCII string.
              sb.Append(Encoding.ASCII.GetString(bytes, 0, i))
              ' read next bytes
              i = stream.Read(bytes, 0, bytes.Length)
            End While
            Console.WriteLine("Server received: {0}", sb.ToString())
            If sb.ToString().Contains("ppp") Then
              Console.WriteLine("Contains ppp")
              ' Send back a response.
              Response(stream, "my_response")
            End If
          End Using
        End Sub
    
        Public Shared Sub Response(stream As Stream, message As String)
          Dim response As Byte() = Encoding.ASCII.GetBytes(message)
          stream.Write(response, 0, response.Length)
          Console.WriteLine("response {0} sent", message)
        End Sub
    
      End Class
    End Module


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks


    Friday, May 17, 2019 8:39 PM