Upd
Upd
Upd
Upd
Upd
Upd Upd Upd Upd Upd Upd Upd Upd
Upd Upd
Upd
Go Back  Xtreme Visual Basic Talk > > > Upd


Reply
 
Thread Tools Display Modes
  #1  
Old 08-06-2014, 04:42 AM
jaccog's Avatar
jaccog jaccog is offline
Regular
 
Join Date: Mar 2005
Location: The Netherlands
Posts: 96
Default Upd


I'm communication with two applications using UDP sockets.
It works just fine...99% of the time, but once in a while I seem to miss a packet at the receiving side.

On the sending end, I check the number of bytes sent in my transmit routine:
Code:
SentBytes = Connection.SendTo(Data, Endpoint)
This is my reference for the number of bytes that have actually been sent.

On the receiving end however, I sometimes receive less packages. When counting the number of bytes received, it seems that I'm missing exactly the number of bytes as one transmission should be long.
I receive my data like this:
Setting-up the Async receive:
Code:
    Private Sub AwaitData()
        BeginReceiveFailed = False
        Try
            SyncLock ConnectionLock
                Dim aSyncState As New StateObject With {.ConnectionSocket = Connection, .RemoteEndpoint = New IPEndPoint(0, 0)}
                aSyncState.AsyncResult = Connection.BeginReceiveFrom(_RxBuffer, 0, _RxBuffer.Length - 1, SocketFlags.None, aSyncState.RemoteEndpoint, 
                                            New AsyncCallback(AddressOf ReceiveCallback), aSyncState)
            End SyncLock
        Catch ex As Exception
            BeginReceiveFailed = True
        End Try
    End Sub
The receivecallback is like this:
Code:
    Private Sub ReceiveCallback(ByVal AR As IAsyncResult)
        Dim aSyncState As StateObject = AR.AsyncState
        Dim ByteCount As Integer

        Try
            SyncLock ConnectionLock
                ByteCount = aSyncState.ConnectionSocket.EndReceiveFrom(AR, aSyncState.RemoteEndpoint)
            End SyncLock
            ProcessBytes(_RxBuffer, ByteCount, aSyncState.RemoteEndpoint)
            System.Threading.Interlocked.Add(_RxBytes, ByteCount)
        Catch ex As Exception
        End Try
        AwaitData()
    End Sub
The ProcessBytes routine handles my data (displaying, starting task, etc).
Could it be that my "ProcessBytes" routine takes too long so that the socket is unable to receive another packet while processing the previous packet?
Packets are max 100 bytes long.

I am on a local LAN with only a single switch, so I don't think the message gets lost in a colission.


Any clue as to why I am missing some messages?

Thanks a bunch!
__________________
Jacco.
-The one who says it cannot be done should never interrupt the one who is doing it-

Last edited by passel; 08-06-2014 at 11:04 AM. Reason: Wrapped a long line so easier to read forum page.
Reply With Quote
  #2  
Old 08-06-2014, 11:00 AM
passel's Avatar
passelUpd passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,018
Default

No clue really. I've only looked at the async callback method briefly and never tried it out yet.
I've used the UDPClient 99% of the time, and haven't had any issue over the years, but you may have a reason for not using it.

My preference is to just start a background thread to do the receiving in, leaving the GUI free to send and react to user input.

I put together a simple example for someone awhile back, where he wanted to send some integer data and a string between two processes.
So the example had a sending side and a receiving side, and I used mouse input on the sending side to get X,Y values, and drew lines on the receiving side.
He later wanted to send data the other direction so I just said do the same thing in the other direction. I copied the code from the sender to the receiver, so now they are really pretty much the same, just port numbers reversed between sending and receiving.
I'll post those two, so you can check them out if curious.

I exceeded the 10,000 character limit, so since both sides are the same except for the port swap, I'll just post the one code and you can copy it into two projects, just swap the final 0 and 1 digit of the port numbers on one of the projects.
(port numbers swapped example)
Code:
  Dim udpSender As New UdpClient(32221)                               'Listen on UDP port 32221
  Dim destIPnt As New IPEndPoint(IPAddress.Parse("127.0.0.1"), 32220) 'Send to UDP port 32220
Code:
Imports System.Threading
Imports System.Net
Imports System.Net.Sockets

Public Class Form1
  Dim udpSender As New UdpClient(32220)                               'Listen on UDP port 32220
  Dim destIPnt As New IPEndPoint(IPAddress.Parse("127.0.0.1"), 32221) 'Send to UDP port 32221

  Dim drawMap As New Bitmap(1200, 1000)
  Dim gMap As Graphics
  Dim receiveThread As New Thread(AddressOf Receiver)
  Dim leaving As Boolean

  Private Sub Form1_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
    If e.Button = Windows.Forms.MouseButtons.Left Then
      Dim s As String = "ThisAtest"
      Dim sl As Integer = s.Length
      Dim b(11 + s.Length) As Byte '11 for X,Y and string size, + length of the string
      Buffer.BlockCopy(BitConverter.GetBytes(e.X), 0, b, 0, 4) 'convert X to 4 byte array and copy to b(0..3)
      Buffer.BlockCopy(BitConverter.GetBytes(e.Y), 0, b, 4, 4) 'convert Y to 4 byte array and copy to b(4..7)
      Buffer.BlockCopy(BitConverter.GetBytes(sl), 0, b, 8, 4) 'convert string length to 4 byte array and copy to b(8..11)
      Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(s), 0, b, 12, s.Length)
      udpSender.Send(b, b.Length, destIPnt)
    End If
  End Sub

  Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    Dim b() As Byte = {0, 0, 0, 0, 0, 0, 0, 0}

    Me.Text = "Drag on this form to draw on the receiving form"
    Me.DoubleBuffered = True
    Me.BackgroundImage = drawMap
    gMap = Graphics.FromImage(drawMap)
    receiveThread.IsBackground = True
    receiveThread.Start()
  End Sub

  Private Sub Receiver()
    Dim remoteIP As New IPEndPoint(IPAddress.Any, 0)  'will hold who sent the data (not utilized)
    Dim b() As Byte 'buffer to receive data
    Dim s As String
    Dim sl As Integer

    Static LastPoint As Point

    Do Until leaving
      b = udpSender.Receive(remoteIP)
      If b.Count > 12 Then 'expect 12 bytes plus string data
        Dim newPoint As Point
        newPoint.X = BitConverter.ToInt32(b, 0)  'convert first 4 bytes (0..3) to Int32 
        newPoint.Y = BitConverter.ToInt32(b, 4)  'convert second 4 bytes (4..7) to Int32
        sl = BitConverter.ToInt32(b, 8)  'get string length
        s = System.Text.ASCIIEncoding.ASCII.GetString(b, 12, sl) 'get ASCII bytes and convert to a string
        Me.Invoke(Sub() Me.Text = s)  'show the string received on the Form's title bar
        gMap.DrawLine(Pens.Black, LastPoint, newPoint)
        LastPoint = newPoint
        Me.BeginInvoke(Sub() Me.Invalidate()) 'Have to use Invoke to do stuff on the GUI thread
      End If
    Loop

  End Sub
End Class
Another more recent experiment had to do with many applications connecting to one, i.e. a multi-client to a server type scenario.
Usually on the server, a socket is used to listen, and when data is received, another socket is created to start communicating with the client that transmitted, so you would have create a socket for every client that connected. In my case, that would be a background thread for each receiver for each socket, etc...
Rather than go that route, since the RemoteIP and Port is available with packet received, I thought I test using just the one socket to receive all packets from all clients, and track the client by RemoteIP and Port.

Now, this is a recent test, only done this week, so is in its first stage, so doesn't clean up after itself, and you need to shutdown the clients first because they don't have an exception handler, but the test worked.
I kicked of six clients and had them run all night and no packets were lost (over 200000 transfers each). Yesterday I opened 20 clients (reduced the window size so only two of the labels were showing so could be packed in a corner of the monitor, running in the background and let run all day). Haven't used two machines with it yet, and the messages are only 8 bytes and only transmitted at about 10hz, so not much of a test at the moment, since even with 20 clients, you're only talking 200 messages in each direction per second, so 400*8 = 3200 bytes worth of data bandwidth per second, which is nothing.

The server has no GUI controls, and neither server or client is a lot of code, but I'll post the code for Server, and attach a zip of the client project since it does have some GUI elements (a button to stop or start the timer which sends data to the server) and some labels, two of which are used. The first label displays a count of each transmit done to the server. The second label displays the count from the server of how many receives it has processed for this particular client. The numbers should match, unless a transfer from client to server is lost. (a lost transfer from server to client won't show because we're not tracking that. The second pair of labels could be used, and and a second counter for the server to increment each transfer could be added if we wanted to track that).
Server Code:
Code:
Imports System.Threading
Imports System.Net
Imports System.Net.Sockets

Public Class Form1

  Private Structure RemoteVehicleConnection
    Public IPandPort As IPEndPoint
    Public HeartBeatCount As Integer
    Public HeartBeatInpCount As Integer
    Public ID As String
  End Structure

  Dim rv As RemoteVehicleConnection
  Dim VehicleList As New List(Of RemoteVehicleConnection)

  Dim socketIn As New UdpClient(12321)                                'Listen on UDP port 12321
  Dim receiveThread As New Thread(AddressOf Receiver)
  Dim leaving As Boolean

  Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    receiveThread.IsBackground = True
    receiveThread.Start()
    Timer1.Enabled = True
  End Sub

  Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    leaving = True
  End Sub

  Private Sub Receiver()
    Dim remoteIP As New IPEndPoint(IPAddress.Any, 0)  'will hold who sent the data 
    Dim b() As Byte 'buffer to receive data
    Dim rvc As RemoteVehicleConnection

    Dim found As Boolean

    Do Until leaving
      remoteIP = New IPEndPoint(IPAddress.Any, 0)
      Try
        b = socketIn.Receive(remoteIP)
        found = False
        Monitor.Enter(VehicleList) 'get exclusive access to the list
        For i As Integer = 0 To VehicleList.Count - 1
          With VehicleList(i)
            If .IPandPort.Port = remoteIP.Port Then
              If .IPandPort.Address.ToString = remoteIP.Address.ToString Then
                found = True
                rvc = VehicleList(i)
                rvc.HeartBeatCount += 1
                rvc.HeartBeatInpCount = BitConverter.ToInt32(b, 0)
                VehicleList(i) = rvc
                Exit For
              End If
            End If
          End With
        Next
        If Not found Then
          rv.IPandPort = remoteIP
          rv.HeartBeatCount = 1
          rv.HeartBeatInpCount = BitConverter.ToInt32(b, 0)
          VehicleList.Add(rv)
          Debug.Print(rv.IPandPort.Port.ToString)
        End If
        Monitor.Exit(VehicleList)
      Catch
      End Try
    Loop

  End Sub

  Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick

    Monitor.Enter(VehicleList) 'get exclusive access to the list
    Dim b(7) As Byte
    For i As Integer = 0 To VehicleList.Count - 1
      With VehicleList(i)
        Buffer.BlockCopy(BitConverter.GetBytes(.HeartBeatCount), 0, b, 0, 4)
        socketIn.Send(b, b.Length, .IPandPort)
      End With
    Next
    Monitor.Exit(VehicleList)
  End Sub
End Class
Client project attached.
Attached Files
File Type: zip UDP_MultiClientTest.zip (17.5 KB, 1 views)
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.

Last edited by passel; 08-06-2014 at 11:12 AM.
Reply With Quote
  #3  
Old 08-06-2014, 03:08 PM
jaccog's Avatar
jaccog jaccog is offline
Regular
 
Join Date: Mar 2005
Location: The Netherlands
Posts: 96
Default

Hi Passel,

Thank you for your elaborated answer.
I tried the blocking receive method using a thread to receive the data and it actually has the same problem.

Googling and asking friends, resulted in an issue which seems to be related to the TCP/UDP CRC offloading capabilities of the NIC.
So, I'm investigating in that direction for now.

Keep you posted.
__________________
Jacco.
-The one who says it cannot be done should never interrupt the one who is doing it-
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
Upd
Upd
Upd Upd
Upd
Upd
Upd Upd Upd Upd Upd Upd Upd
Upd
Upd
 
Upd
Upd
 
-->