locked
Asynchronous Operator | TCP | Serialization RRS feed

  • Question

  • Hello dear Sirs, 

    I need your help to make my server and client asynch.

    Here is the code 

    Dll to serialize : 

    Public Class Class1
        <Serializable()>
        Public Class PacketMaker
            Public Property File As Byte()
            Public Property Type_Packet As PacketType
        End Class
        Public Enum PacketType As Integer
            MSG = &H5057
            File = &H5056
        End Enum
    End Class

    Server : 

    Imports System.Net
    Imports System.Net.Sockets
    Imports System.Runtime.Serialization.Formatters.Binary
    Imports System.Text
    Imports System.Threading
    Imports ClassLibrary1.Class1
    Public Class Form1
        Public T As TcpListener = New TcpListener(IPAddress.Any, 8080)
        Private client As TcpClient
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            T.Start()
            Task.Run(Sub() D())
        End Sub
        Public Sub D()
            While True
                client = T.AcceptTcpClient()
                Task.Run(Sub() ThreadProc2(client))
    
               Task.Run(Sub() ThreadProc(client))
    
            End While
        End Sub
    
        Private Sub ThreadProc(ByVal obj As Object)
            Dim client = CType(obj, TcpClient)
            Dim B As BinaryFormatter = New BinaryFormatter()
            Dim p As PacketMaker = New PacketMaker()
            p.Type_Packet = PacketType.MSG
            p.File = IO.File.ReadAllBytes("File1.zip")
            B.Serialize(client.GetStream(), p)
        End Sub
    
        Private Sub ThreadProc2(ByVal obj As Object)
            Dim client = CType(obj, TcpClient)
            Dim B As BinaryFormatter = New BinaryFormatter()
            Dim p As PacketMaker = New PacketMaker()
            p.Type_Packet = PacketType.File
            p.File = IO.File.ReadAllBytes("File2.zip")
            B.Serialize(client.GetStream(), p)
    
        End Sub
    
    End Class

    Client : 

    Imports System.Net
    Imports System.Net.Sockets
    Imports System.Runtime.Serialization.Formatters.Binary
    Imports ClassLibrary1.Class1
    
    Public Class Form1
        Private T As TcpClient = New TcpClient()
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim address As String = "192.168.0.15"
            T.Connect(IPAddress.Parse(address), 8080)
            Dim N As NetworkStream = T.GetStream()
            Task.Run(Sub() R(N))
        End Sub
        Public  Sub R(ByVal L As NetworkStream)
            Dim sf As BinaryFormatter = New BinaryFormatter()
    
            While True
    
                Dim po As PacketMaker = CType(sf.Deserialize(L), PacketMaker)
        
    
               IO.File.WriteAllBytes(po.Type_Packet.ToString(), po.File)
            End While
        End Sub
    
    
    End Class

    The server send 2 files and client need to receive them but here nothing is happening. No file is written (MSG & FILE). The only way to avoid problem I have found is to set a synclock here :

      Private Sub ThreadProc(ByVal obj As Object)
    
            SyncLock T
    
                Dim client = CType(obj, TcpClient)
            Dim B As BinaryFormatter = New BinaryFormatter()
            Dim p As PacketMaker = New PacketMaker()
            p.Type_Packet = PacketType.MSG
            p.File = IO.File.ReadAllBytes("File1.zip")
                B.Serialize(client.GetStream(), p)
            End SyncLock
        End Sub
    
        Private Sub ThreadProc2(ByVal obj As Object)
    
            SyncLock T
    
                Dim client = CType(obj, TcpClient)
            Dim B As BinaryFormatter = New BinaryFormatter()
            Dim p As PacketMaker = New PacketMaker()
            p.Type_Packet = PacketType.File
            p.File = IO.File.ReadAllBytes("File2.zip")
            B.Serialize(client.GetStream(), p)
    
            End SyncLock
    
        End Sub





    • Edited by Arsium Thursday, November 26, 2020 2:49 PM
    Thursday, November 26, 2020 2:34 PM

All replies

  • Hello,

    The following are suggestions, incomplete but are solid ideas to use with asynchronous operations. Don't have time to write up a code sample.

    Make sure to read the following to get a handle on this type of coding.

    Consider using the following where the method need to be marked with Async

    Dim client As TcpClient = Await listener.AcceptTcpClientAsync()

    I'd use a function e.g.

    Public Async Function Run() As Task

    And it may be beneficial to have a way to cancel the operation

    Public Async Function Run(ByVal cancellationToken As CancellationToken) As Task
    Finally consider using a TraceListener to log operations, I have a simple/basic example here.


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    My GitHub code samples
    GitHub page

    Check out:  the new Microsoft Q&A forums

    • Proposed as answer by KHURRAM RAHIM Friday, November 27, 2020 10:59 AM
    • Unproposed as answer by Arsium Friday, November 27, 2020 11:33 AM
    Thursday, November 26, 2020 4:06 PM
  • So according to : Asynchronous Programming with Async and Await - Visual Basic | Microsoft Docs I can't use TcpClient | Listener Async with binaryformatter and serialization ?
    Thursday, November 26, 2020 9:13 PM
  • Hi Arsium,

    We’re doing research on this issue. It might take some time before we get back to you.

    Thank you for your patience

    Best Regards,

    Xingyu Zhao


    Visual Basic and CLR forum will be migrating to a new home on Microsoft Q&A! (VB.NET and CLR) We invite you to post new questions in the new home on Microsoft Q&A ! For more information, please refer to the sticky post(VB.NET and CLR).

    Friday, November 27, 2020 9:27 AM
  • Okay okay no problem hope you will find an idea / solution
    Saturday, November 28, 2020 11:43 AM
  • Hi Arsium,

    Did you consider using asynchronous socket?

    Here are two related references you can refer to.

    1. Using an Asynchronous Server Socket
    2. Using an Asynchronous Client Socket

    Besides, here's an example of asynchronous tcp server you may need.

        Private Async Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)
            Await Task.Run(Sub() RunServerAsync())
        End Sub
        Private Async Sub RunServerAsync()
            Dim listener = New TcpListener(IPAddress.Any, 8080)
            listener.Start()
    
            Try
                While True
                    Await Accept(Await listener.AcceptTcpClientAsync())
                End While
            Finally
                listener.Stop()
            End Try
        End Sub
    
        Private Async Function Accept(ByVal client As TcpClient) As Task
            Await Task.Yield()
            Try
                Using client
                    Using n As NetworkStream = client.GetStream()
                        Dim send_data As Byte()
                        Dim bf As BinaryFormatter = New BinaryFormatter()
    
                        Using ms = New MemoryStream()
                            bf.Serialize(ms, yourObject)
                            send_data = ms.ToArray()
                        End Using
    
                        Await n.WriteAsync(send_data, 0, send_data.Length)
                    End Using
                End Using
    
            Catch ex As Exception
                Console.WriteLine(ex.Message)
            End Try
        End Function

    Best Regards,

    Xingyu Zhao


    Visual Basic and CLR forum will be migrating to a new home on Microsoft Q&A! (VB.NET and CLR) We invite you to post new questions in the new home on Microsoft Q&A ! For more information, please refer to the sticky post(VB.NET and CLR).


    Tuesday, December 1, 2020 9:15 AM
  • Okay okay I misspoke my request : is it possible to serialize in parallel (not async) while using binaryformatter and network stream ?



    • Edited by Arsium Tuesday, December 1, 2020 8:40 PM
    Tuesday, December 1, 2020 10:30 AM
  • Okay okay I misspoke my request : is it possible to serialize in parallel (not async) while using binaryformatter and network stream ? This causes my problem (not async) I'm beginner sry

    Tuesday, December 1, 2020 8:40 PM
  • Hi Arsium,

    >>is it possible to serialize in parallel (not async) while using binaryformatter and network stream ?

    Yes, when you send multiple messages using TCP, you need to split the message at the other end in some way.

    As you can see in the following code, I save each length of the message to byte array with a fixed length of 4.

    Server:

    Public Class Form1
        Private port As Integer = 8080
        Private _running As Boolean
        Private listener As TcpListener
        Private connectedTcpClient As TcpClient
        Private _bFormatter As BinaryFormatter = New BinaryFormatter()
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            listener = New TcpListener(IPAddress.Any, port)
        End Sub
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            If Not _running Then
                listener.Start()
                Me._running = True
                Task.Factory.StartNew(Sub()
                                          ListenForClientConnections()
                                      End Sub)
            End If
        End Sub
        Private Sub ListenForClientConnections()
            While Me._running
                Me.connectedTcpClient = listener.AcceptTcpClient()
                Dim networkStream As NetworkStream = Me.connectedTcpClient.GetStream()
    
                Dim p As PacketMaker = New PacketMaker()
                p.Type_Packet = PacketType.MSG
                p.File = File.ReadAllBytes("D:\test.txt") 'File1.zip
                Dim p2 As PacketMaker = New PacketMaker()
                p2.Type_Packet = PacketType.File
                p2.File = File.ReadAllBytes("D:\test2.txt") 'File2.zip
    
                SendMessage(_bFormatter, networkStream, p)
                SendMessage(_bFormatter, networkStream, p2)
            End While
        End Sub
    
        Private Sub SendMessage(ByVal _bFormatter As BinaryFormatter, ByVal networkStream As NetworkStream, ByVal p As PacketMaker)
            Dim header As Byte() = New Byte(3) {}
            Dim send_dataLen As Integer = 0
            Dim send_data As Byte()
    
            Using ms = New MemoryStream()
                _bFormatter.Serialize(ms, p)
                send_data = ms.ToArray()
                send_dataLen = ms.ToArray().Length
            End Using
            ' Convert the integer type length value to byte array and then send it.
            header = convertToByteArray(header, send_dataLen)
            networkStream.Write(header, 0, header.Length)
            networkStream.Write(send_data, 0, send_data.Length)
        End Sub
    
        Private Function convertToByteArray(ByVal header As Byte(), ByVal i As Integer) As Byte()
            Dim swi As Switcher = New Switcher()
            swi.intVal = i
            header(0) = swi.b0
            header(1) = swi.b1
            header(2) = swi.b2
            header(3) = swi.b3
            Return header
        End Function
        Public Sub StopListen()
            If Me._running Then
                listener.Stop()
                Me._running = False
            End If
        End Sub
    End Class
    
    <StructLayout(LayoutKind.Explicit)>
    Structure Switcher
        <FieldOffset(0)>
        Public intVal As Integer
        <FieldOffset(0)>
        Public b0 As Byte
        <FieldOffset(1)>
        Public b1 As Byte
        <FieldOffset(2)>
        Public b2 As Byte
        <FieldOffset(3)>
        Public b3 As Byte
    End Structure

    Client:

        Private address As String = "your ip address"
        Private port As Integer = 8080
        Private client As TcpClient = New TcpClient()
        Private _bFormatter As BinaryFormatter = New BinaryFormatter()
        Private _running As Boolean
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            client.Connect(IPAddress.Parse(address), port)
            StartListening()
        End Sub
        Public Sub StartListening()
            SyncLock Me
    
                If Not _running Then
                    Me._running = True
                    Task.Factory.StartNew(Sub()
                                              ListenForMessage()
                                          End Sub)
                Else
                    Me._running = True
                    Task.Factory.StartNew(Sub()
                                              ListenForMessage()
                                          End Sub)
                End If
            End SyncLock
        End Sub
    
        Private Sub ListenForMessage()
            While Me._running
                Dim networkStream As NetworkStream = client.GetStream()
                Dim header As Byte() = New Byte(3) {}
                Dim receive_data As Byte() = New Byte(65534) {}
                Dim packageLen As Integer = 0
                Dim offset As Integer = 0
                Dim bytesRead As Integer = networkStream.Read(receive_data, 0, receive_data.Length)
                Dim count As Integer = 3
    
                While bytesRead <> 0 AndAlso bytesRead > offset
    
                    Using ms = New MemoryStream()
                        ms.Write(receive_data, offset, 4)
                        header = ms.ToArray()
                        ' Convert length of message byte array to the integer 
                        packageLen = BitConverter.ToInt32(header, 0)
                        ms.Seek(0, SeekOrigin.Begin)
                        ms.Write(receive_data, offset + 4, packageLen)
                        ms.Seek(0, SeekOrigin.Begin)
                        Dim po As PacketMaker = CType(_bFormatter.Deserialize(ms), PacketMaker)
                        File.WriteAllBytes("D:\test" & count & ".txt", po.File)
                        count += 1
                    End Using
    
                    offset += packageLen + 4
                End While
            End While
        End Sub

    I use two txt files to make the test.

    Content in two files.

    Result of my test.

    Hope it could be helpful.

    Best Regards,

    Xingyu Zhao

     

    Visual Basic and CLR forum will be migrating to a new home on Microsoft Q&A! (VB.NET and CLR) We invite you to post new questions in the new home on Microsoft Q&A ! For more information, please refer to the sticky post(VB.NET and CLR).





    Wednesday, December 2, 2020 9:54 AM
  • Okay okay thx I will try that !
    Thursday, December 3, 2020 5:42 PM
  • Hello Sir , 

    I've tried your code and nothing is happening with those sent files...I tried with 200MB for first file and 8MB for second and nothing happened. However , it seems working with low size files.




    • Edited by Arsium Saturday, December 5, 2020 12:23 PM
    Saturday, December 5, 2020 12:09 PM