Results 1 to 8 of 8

Thread: [RESOLVED] Strange UDP Issue

  1. #1

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,102

    Resolved [RESOLVED] Strange UDP Issue

    Here's a UDP class:

    vb Code:
    1. Public Class UDPCommon
    2.     Implements IDisposable
    3.  
    4.     'The UDP listening components.
    5.     Private WithEvents locUDP As System.Net.Sockets.Socket
    6.     Private WithEvents locUDPOut As System.Net.Sockets.UdpClient
    7.     Private ThreadListen As New Threading.Thread(AddressOf WaitForPending)
    8.     Private timeCount As Integer
    9.     Private broad As System.Net.IPEndPoint
    10.  
    11.  
    12.     'The dataQueue
    13.     Private dgList As Queue(Of DataGramReceiver)
    14.  
    15.     'The result
    16.     Private disposedValue As Boolean = False        ' To detect redundant calls
    17.  
    18.     'Synchronization.
    19.     Private Shared myContext As System.Threading.SynchronizationContext
    20.  
    21. #Region "Constructors and Destructors"
    22.  
    23.     Public Sub New()
    24.         'Set up the queue.
    25.         dgList = New Queue(Of DataGramReceiver)
    26.         locUDPOut = New Net.Sockets.UdpClient(11013)
    27.         locUDP = New Net.Sockets.Socket(Net.Sockets.AddressFamily.InterNetwork, Net.Sockets.SocketType.Dgram, Net.Sockets.ProtocolType.Udp)
    28.         locUDP.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, True)
    29.         locUDP.Bind((New Net.IPEndPoint(Net.IPAddress.Any, 11013)))
    30.  
    31.         locUDPOut.EnableBroadcast = True
    32.  
    33.         broad = New System.Net.IPEndPoint(System.Net.IPAddress.Broadcast, 11013)
    34.  
    35.         myContext = System.Threading.SynchronizationContext.Current
    36.  
    37.         'Start listening.
    38.         ThreadListen.Name = "Listening Thread"
    39.         ThreadListen.Start()
    40.     End Sub
    41.  
    42.     ' IDisposable
    43.     Protected Overridable Sub Dispose(ByVal disposing As Boolean)
    44.         If Not Me.disposedValue Then
    45.             If disposing Then
    46.                 ThreadListen.Abort()
    47.                 locUDP.Close()
    48.                 locUDPOut.Close()
    49.             End If
    50.  
    51.             ' TODO: free shared unmanaged resources
    52.         End If
    53.         Me.disposedValue = True
    54.     End Sub
    55.  
    56. #Region " IDisposable Support "
    57.     ' This code added by Visual Basic to correctly implement the disposable pattern.
    58.     Public Sub Dispose() Implements IDisposable.Dispose
    59.         ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    60.         Dispose(True)
    61.         GC.SuppressFinalize(Me)
    62.     End Sub
    63. #End Region
    64.  
    65. #End Region
    66.  
    67. #Region "Properties and Events"
    68.  
    69.     Public Event MessageIn()
    70.  
    71.     Public ReadOnly Property MessageCount() As Integer
    72.         Get
    73.             Return dgList.Count
    74.         End Get
    75.     End Property
    76.  
    77.     Public ReadOnly Property NextMessage() As DataGramReceiver
    78.         Get
    79.             Return dgList.Dequeue
    80.         End Get
    81.     End Property
    82.  
    83. #End Region
    84.  
    85. #Region "Public Functions"
    86.  
    87.     'This will get the next DGR from the queue.
    88.     Public Function GetNext() As DataGramReceiver
    89.         Dim dgRet As New DataGramReceiver
    90.  
    91.         Threading.Monitor.Enter(Me)
    92.         Try
    93.             If dgList.Count > 0 Then
    94.                 dgRet = New DataGramReceiver(dgList.Dequeue)
    95.             End If
    96.         Catch ex As Exception
    97.             Windows.Forms.MessageBox.Show(ex.Message, "Error Getting DataGramReceiver", MessageBoxButtons.OK, MessageBoxIcon.Error)
    98.         Finally
    99.             Threading.Monitor.Exit(Me)
    100.         End Try
    101.  
    102.         'It may be the default, or it may not, either way it is a good local copy in the calling thread, return it.
    103.         Return dgRet
    104.  
    105.     End Function
    106.  
    107.     'Empties the queue
    108.     Public Sub Flush()
    109.         Threading.Monitor.Enter(Me)
    110.         Try
    111.             dgList.Clear()
    112.         Finally
    113.             Threading.Monitor.Exit(Me)
    114.         End Try
    115.     End Sub
    116.  
    117.     Public Sub SendMessage(ByVal btArr() As Byte, ByVal toWho As Byte, ByVal what As Byte)
    118.         Dim lBtArr(btArr.Length + 2) As Byte
    119.  
    120.         lBtArr(0) = 100 'This is the OL ID.
    121.         lBtArr(1) = toWho
    122.         lBtArr(2) = what
    123.  
    124.         System.Array.Copy(btArr, 0, lBtArr, 3, btArr.Length)
    125.         'NOTE: NEED TO CHECK THAT THIS IS DOING AS IT SHOULD.
    126.  
    127.         locUDP.SendTo(lBtArr, lBtArr.Length, Net.Sockets.SocketFlags.Broadcast, broad)
    128.  
    129.     End Sub
    130.  
    131. #End Region
    132.  
    133. #Region "Private Functions"
    134.  
    135.     Private Sub WaitForPending()
    136.         Dim bHolder(511) As Byte
    137.         Dim numIn As Integer
    138.         Dim dgRec As DataGramReceiver
    139.         Dim bTrans() As Byte
    140.  
    141.         While True
    142.             If CBool(locUDP.Available) Then
    143.                 numIn = locUDP.Receive(bHolder, 512, Net.Sockets.SocketFlags.None)
    144.                 If numIn > 4 Then
    145.                     If bHolder(1) = 0 OrElse bHolder(1) = 100 Then
    146.                         'Set the holder up to the right size.
    147.                         ReDim bTrans(numIn)
    148.                         Array.Copy(bHolder, 3, bTrans, 0, numIn - 3)
    149.                         dgRec = New DataGramReceiver(128, bHolder(2), bTrans)
    150.                         Add(dgRec)
    151.                     End If
    152.                 End If
    153.             End If
    154.         End While
    155.     End Sub
    156.  
    157.     'Adds one DGR to the queue
    158.     Private Sub Add(ByVal dgR As DataGramReceiver)
    159.         'Set the monitor.
    160.         Threading.Monitor.Enter(Me)
    161.         Try
    162.             dgList.Enqueue(dgR) 'That's all that happens.
    163.         Finally
    164.             Threading.Monitor.Exit(Me)
    165.         End Try
    166.         myContext.Post(AddressOf GotData, Nothing)
    167.     End Sub
    168.  
    169.     'This is invoked by the other thread.
    170.     Private Sub GotData(ByVal state As Object)
    171.         RaiseEvent MessageIn()
    172.     End Sub
    173.  
    174.  
    175. #End Region
    176.  
    177. End Class

    The intention for this class is that it will be included in multiple modules on multiple computers. I have tried it on two different computers, but not in talking to each other, I haven't gotten that far. On both computers I have projects that have an instance of this class along with an instance of a class that is similar, but uses two ports rather than one, because that other class will have only one module writing to one port and listening to the other port, while all the other modules will listen to the first port and write to the second. That was working fine with two modules...now we'll see, but not yet.

    Therefore, on both systems there is one instance of this class, and one instance of the class with two ports. On the system running XP Home (I have no idea whether or not that matters), if I step through the two lines that create the instances of the two classes, the first class is created pretty much instantly, while the class posted above takes a tiny fraction of a second. There is a hesitation, but it is very brief.

    On the system running XP Pro, the two port class is also created instantly. Both classes are in Try blocks for various reasons, but the first class is created without any issues. When I step over the line that creates the class posted above, there is a pause of a second or two (which is an ENORMOUS length of time), then the next line highlighted is a function in the catch block, but the function isn't executed, and no exception was actually raised. I've NEVER seen this happen before, where VS will incorrectly highlight the next line.

    If I run this without adding a breakpoint, the form takes up to five seconds to be displayed. Since there is nothing else that takes any time, I assume the entire issue is the class posted above. Frankly, I've tinkered around with this class so many times by now that I forget whether it is even actually valid now, but it throws no exceptions.

    If I step through the creation of the class, there are a few minor pauses, such as when the constructor starts the listener thread, but the pauses are minor. The delay occurs after the New sub returns, and the execution highlight is, again, taken to the first line in the catch block, though no exception was raised, and the line in the catch block is not actually executed.

    Any suggestions? Any alternatives? The key is to have a class that can read and write to a single port using UDP, and created such that there could be multiple instances of the class in different modules, all running simultaneously on the same system.
    My usual boring signature: Nothing

  2. #2
    Raging swede Atheist's Avatar
    Join Date
    Aug 2005
    Location
    Sweden
    Posts
    8,018

    Re: Strange UDP Issue

    Hey SH.
    I trying your class but its throwing an SocketException on this line
    locUDP.Bind((New Net.IPEndPoint(Net.IPAddress.Any, 11013)))
    The exception is:
    An attempt was made to access a socket in a way forbidden by its access permissions.
    I dont know if this is due to any security settings in Vista, but its defenitly caused by the fact that the UdpClient is already using that address. Have you gotten this?
    Rate posts that helped you. I do not reply to PM's with coding questions.
    How to Get Your Questions Answered
    Current project: tunaOS
    Me on.. BitBucket, Google Code, Github (pretty empty)

  3. #3

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,102

    Re: Strange UDP Issue

    Nope, that's a totally new error for me. On XP Home or Pro, I get no errors in the constructor with the posted code. However, I do get that bizarre behavior during the construction. That gives me something more to look at. This is beginning to look like an interaction between UDP sockets and OS versions.
    My usual boring signature: Nothing

  4. #4

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,102

    Re: Strange UDP Issue

    The working UDP class:

    vb Code:
    1. Public Class UDPCommon
    2.     Implements IDisposable
    3.  
    4.     'The UDP listening components.
    5.     Private WithEvents locUDP As System.Net.Sockets.Socket
    6.     Private WithEvents locUDPOut As System.Net.Sockets.UdpClient
    7.     Private ThreadListen As New Threading.Thread(AddressOf WaitForPending)
    8.     Private timeCount As Integer
    9.     Private broad As System.Net.IPEndPoint
    10.  
    11.  
    12.     'The dataQueue
    13.     Private dgList As Queue(Of DataGramReceiver)
    14.  
    15.     'The result
    16.     Private disposedValue As Boolean = False        ' To detect redundant calls
    17.  
    18.     'Synchronization.
    19.     Private Shared myContext As System.Threading.SynchronizationContext
    20.  
    21. #Region "Constructors and Destructors"
    22.  
    23.     Public Sub New()
    24.         'Set up the queue.
    25.         dgList = New Queue(Of DataGramReceiver)
    26.         locUDPOut = New Net.Sockets.UdpClient()
    27.         locUDPOut.Client.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, True)
    28.         locUDP = New Net.Sockets.Socket(Net.Sockets.AddressFamily.InterNetwork, Net.Sockets.SocketType.Dgram, Net.Sockets.ProtocolType.Udp)
    29.         locUDP.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, True)
    30.         locUDP.Bind((New Net.IPEndPoint(Net.IPAddress.Any, 11000)))
    31.  
    32.         locUDPOut.EnableBroadcast = True
    33.  
    34.         broad = New System.Net.IPEndPoint(System.Net.IPAddress.Broadcast, 11000)
    35.  
    36.         myContext = System.Threading.SynchronizationContext.Current
    37.  
    38.         'Start listening.
    39.         ThreadListen.Name = "Listening Thread"
    40.         ThreadListen.Start()
    41.     End Sub
    42.  
    43.     ' IDisposable
    44.     Protected Overridable Sub Dispose(ByVal disposing As Boolean)
    45.         If Not Me.disposedValue Then
    46.             If disposing Then
    47.                 ThreadListen.Abort()
    48.                 locUDP.Close()
    49.                 locUDPOut.Close()
    50.             End If
    51.  
    52.             ' TODO: free shared unmanaged resources
    53.         End If
    54.         Me.disposedValue = True
    55.     End Sub
    56.  
    57. #Region " IDisposable Support "
    58.     ' This code added by Visual Basic to correctly implement the disposable pattern.
    59.     Public Sub Dispose() Implements IDisposable.Dispose
    60.         ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    61.         Dispose(True)
    62.         GC.SuppressFinalize(Me)
    63.     End Sub
    64. #End Region
    65.  
    66. #End Region
    67.  
    68. #Region "Properties and Events"
    69.  
    70.     Public Event MessageIn()
    71.  
    72.     Public ReadOnly Property MessageCount() As Integer
    73.         Get
    74.             Return dgList.Count
    75.         End Get
    76.     End Property
    77.  
    78.     Public ReadOnly Property NextMessage() As DataGramReceiver
    79.         Get
    80.             Return dgList.Dequeue
    81.         End Get
    82.     End Property
    83.  
    84. #End Region
    85.  
    86. #Region "Public Functions"
    87.  
    88.     'This will get the next DGR from the queue.
    89.     Public Function GetNext() As DataGramReceiver
    90.         Dim dgRet As New DataGramReceiver
    91.  
    92.         Threading.Monitor.Enter(Me)
    93.         Try
    94.             If dgList.Count > 0 Then
    95.                 dgRet = New DataGramReceiver(dgList.Dequeue)
    96.             End If
    97.         Catch ex As Exception
    98.             Windows.Forms.MessageBox.Show(ex.Message, "Error Getting DataGramReceiver", MessageBoxButtons.OK, MessageBoxIcon.Error)
    99.         Finally
    100.             Threading.Monitor.Exit(Me)
    101.         End Try
    102.  
    103.         'It may be the default, or it may not, either way it is a good local copy in the calling thread, return it.
    104.         Return dgRet
    105.  
    106.     End Function
    107.  
    108.     'Empties the queue
    109.     Public Sub Flush()
    110.         Threading.Monitor.Enter(Me)
    111.         Try
    112.             dgList.Clear()
    113.         Finally
    114.             Threading.Monitor.Exit(Me)
    115.         End Try
    116.     End Sub
    117.  
    118.     Public Sub SendMessage(ByVal btArr() As Byte, ByVal toWho As Byte, ByVal what As Byte)
    119.         Dim lBtArr(btArr.Length + 2) As Byte
    120.  
    121.         lBtArr(0) = 99 'This is the OL ID.
    122.         lBtArr(1) = toWho
    123.         lBtArr(2) = what
    124.  
    125.         System.Array.Copy(btArr, 0, lBtArr, 3, btArr.Length)
    126.         'NOTE: NEED TO CHECK THAT THIS IS DOING AS IT SHOULD.
    127.  
    128.         locUDPOut.Send(lBtArr, lBtArr.Length, broad)
    129.  
    130.     End Sub
    131.  
    132. #End Region
    133.  
    134. #Region "Private Functions"
    135.  
    136.     Private Sub WaitForPending()
    137.         Dim bHolder(511) As Byte
    138.         Dim numIn As Integer
    139.         Dim dgRec As DataGramReceiver
    140.         Dim bTrans() As Byte
    141.  
    142.         While True
    143.             If CBool(locUDP.Available) Then
    144.                 numIn = locUDP.Receive(bHolder, 512, Net.Sockets.SocketFlags.None)
    145.                 If numIn > 4 Then
    146.                     If bHolder(0) <> 99 AndAlso bHolder(1) = 0 OrElse bHolder(1) = 100 Then
    147.                         'Set the holder up to the right size.
    148.                         ReDim bTrans(numIn)
    149.                         Array.Copy(bHolder, 3, bTrans, 0, numIn - 3)
    150.                         dgRec = New DataGramReceiver(128, bHolder(2), bTrans)
    151.                         Add(dgRec)
    152.                     End If
    153.                 End If
    154.             End If
    155.         End While
    156.     End Sub
    157.  
    158.     'Adds one DGR to the queue
    159.     Private Sub Add(ByVal dgR As DataGramReceiver)
    160.         'Set the monitor.
    161.         Threading.Monitor.Enter(Me)
    162.         Try
    163.             dgList.Enqueue(dgR) 'That's all that happens.
    164.         Finally
    165.             Threading.Monitor.Exit(Me)
    166.         End Try
    167.         myContext.Post(AddressOf GotData, Nothing)
    168.     End Sub
    169.  
    170.     'This is invoked by the other thread.
    171.     Private Sub GotData(ByVal state As Object)
    172.         RaiseEvent MessageIn()
    173.     End Sub
    174.  
    175.  
    176. #End Region
    177.  
    178. End Class

    This class is not useful to anybody else as is, because much of the code is HIGHLY specific. However, the general style may well be of use to somebody. The items that should be noted for reuse of this class are these:

    1) The port number is hardcoded in this example to 11000. It would make the class more generic to accept the port number as an argument to the constructor.

    2) The whole bit about the DataGramReceiver may actually be of use to people, but if not, then pretty much everything other than the names of the functions and the constructor, may as well be deleted. The DataGramReceiver is a class with these pieces:

    Byte(0): Who sent the data (every module needs its own ID number).
    Byte(1): Who should receive the data (I have 0 being EVERYBODY).
    Byte(2): The type of the message (see below)

    The other part of the DataGramReceiver is an array of bytes. The consumer checks the type of the message to decide whether or not the byte array needs to be deserialized, and if so, what type of structure it should be deserialized into. By doing this, I allow for the transfer of any arbitrarily sized amount of information between modules throughout the LAN that this runs on.
    My usual boring signature: Nothing

  5. #5
    Raging swede Atheist's Avatar
    Join Date
    Aug 2005
    Location
    Sweden
    Posts
    8,018

    Re: [RESOLVED] Strange UDP Issue

    So its fixed? Good to hear. I wish I could've been of more help though..
    Rate posts that helped you. I do not reply to PM's with coding questions.
    How to Get Your Questions Answered
    Current project: tunaOS
    Me on.. BitBucket, Google Code, Github (pretty empty)

  6. #6

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,102

    Re: [RESOLVED] Strange UDP Issue

    By the way, don't use the class I posted unless you really want to, I am currently VERY close to completing one that doesn't use polling on the listening thread. The problem with polling, even in a background thread, is that it will use tons of CPU time, which sucks. The class posted above uses 100% of CPU time, and running multiple instances will lag even a good system, though perhaps not noticeably. I have currently written, and tested, a version that takes 0% of the CPU when no messages are being sent. It still requires a little optimization, and has only been tested in some situations, but I am running off to a party, and won't finish it up today. The above class is all I am posting, for now.
    My usual boring signature: Nothing

  7. #7

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,102

    Re: [RESOLVED] Strange UDP Issue

    Here is a vastly improved, and pretty much self-contained UDP class that doesn't use polling. The major difference is that the listening thread blocks until a message is received, queues up the message, then starts a new listening thread on the same socket to await the next incoming message. Some internet posts suggest that a drawback of Receive is that it blocks, and will block forever without timing out. However, for a server type of behavior, that's entirely acceptable. The key is that the class be properly disposed so that the thread gets aborted.

    I also improved the class to accept a port number in the constructor. The class only deals with broadcasts rather than specific peer-to-peer communication, but if you want to spread the message to multiple listeners, then broadcast is the way to go. A person might alter this to use multicast, but I saw no advantage in doing so for my purposes.

    vb Code:
    1. Public Class UDPCommon
    2.     Implements IDisposable
    3.  
    4.     'The UDP listening components.
    5.     Private WithEvents locUDP As System.Net.Sockets.Socket
    6.     Private WithEvents locUDPOut As System.Net.Sockets.UdpClient
    7.     Private ThreadListen As Threading.Thread
    8.     Private timeCount As Integer
    9.     Private broad As System.Net.IPEndPoint
    10.     Private whoAmI As Byte
    11.  
    12.  
    13.     'The dataQueue
    14.     Private dgList As Queue(Of DataGramReceiver)
    15.  
    16.     'The result
    17.     Private disposedValue As Boolean = False        ' To detect redundant calls
    18.  
    19.     'Synchronization.
    20.     Private Shared myContext As System.Threading.SynchronizationContext
    21.  
    22. #Region "Constructors and Destructors"
    23.  
    24.     Public Sub New(ByVal myAdd As Byte, ByVal myPort As Integer)
    25.         'Set up the queue.
    26.         whoAmI = myAdd
    27.         dgList = New Queue(Of DataGramReceiver)
    28.         locUDPOut = New Net.Sockets.UdpClient()
    29.         locUDPOut.Client.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, True)
    30.         locUDP = New Net.Sockets.Socket(Net.Sockets.AddressFamily.InterNetwork, Net.Sockets.SocketType.Dgram, Net.Sockets.ProtocolType.Udp)
    31.         locUDP.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, True)
    32.         locUDP.Bind((New Net.IPEndPoint(Net.IPAddress.Any, myPort)))
    33.  
    34.         locUDPOut.EnableBroadcast = True
    35.  
    36.         broad = New System.Net.IPEndPoint(System.Net.IPAddress.Broadcast, myPort)
    37.  
    38.         myContext = System.Threading.SynchronizationContext.Current
    39.  
    40.         'Start listening.
    41.         ThreadListen = New Threading.Thread(AddressOf WaitForPending)
    42.         ThreadListen.Name = "Listening Thread"
    43.         ThreadListen.Start()
    44.     End Sub
    45.  
    46.     ' IDisposable
    47.     Protected Overridable Sub Dispose(ByVal disposing As Boolean)
    48.         If Not Me.disposedValue Then
    49.             If disposing Then
    50.                 ThreadListen.Abort()
    51.                 locUDP.Close()
    52.                 locUDPOut.Close()
    53.             End If
    54.  
    55.             ' TODO: free shared unmanaged resources
    56.         End If
    57.         Me.disposedValue = True
    58.     End Sub
    59.  
    60. #Region " IDisposable Support "
    61.     ' This code added by Visual Basic to correctly implement the disposable pattern.
    62.     Public Sub Dispose() Implements IDisposable.Dispose
    63.         ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    64.         Dispose(True)
    65.         GC.SuppressFinalize(Me)
    66.     End Sub
    67. #End Region
    68.  
    69. #End Region
    70.  
    71. #Region "Properties and Events"
    72.  
    73.     Public Event MessageIn()
    74.  
    75.     Public ReadOnly Property MessageCount() As Integer
    76.         Get
    77.             Return dgList.Count
    78.         End Get
    79.     End Property
    80.  
    81.     Public ReadOnly Property NextMessage() As DataGramReceiver
    82.         Get
    83.             Return dgList.Dequeue
    84.         End Get
    85.     End Property
    86.  
    87. #End Region
    88.  
    89. #Region "Public Functions"
    90.  
    91.     'This will get the next DGR from the queue.
    92.     Public Function GetNext() As DataGramReceiver
    93.         Dim dgRet As New DataGramReceiver
    94.  
    95.         Threading.Monitor.Enter(Me)
    96.         Try
    97.             If dgList.Count > 0 Then
    98.                 dgRet = New DataGramReceiver(dgList.Dequeue)
    99.             End If
    100.         Catch ex As Exception
    101.             Windows.Forms.MessageBox.Show(ex.Message, "Error Getting DataGramReceiver", MessageBoxButtons.OK, MessageBoxIcon.Error)
    102.         Finally
    103.             Threading.Monitor.Exit(Me)
    104.         End Try
    105.  
    106.         'It may be the default, or it may not, either way it is a good local copy in the calling thread, return it.
    107.         Return dgRet
    108.  
    109.     End Function
    110.  
    111.     'Empties the queue
    112.     Public Sub Flush()
    113.         Threading.Monitor.Enter(Me)
    114.         Try
    115.             dgList.Clear()
    116.         Finally
    117.             Threading.Monitor.Exit(Me)
    118.         End Try
    119.     End Sub
    120.  
    121.     Public Sub SendMessage(ByVal btArr() As Byte, ByVal toWho As Byte, ByVal what As Byte)
    122.         Dim lBtArr(btArr.Length + 2) As Byte
    123.  
    124.         lBtArr(0) = whoAmI
    125.         lBtArr(1) = toWho
    126.         lBtArr(2) = what
    127.  
    128.         System.Array.Copy(btArr, 0, lBtArr, 3, btArr.Length)
    129.         'NOTE: NEED TO CHECK THAT THIS IS DOING AS IT SHOULD.
    130.  
    131.         locUDPOut.Send(lBtArr, lBtArr.Length, broad)
    132.  
    133.     End Sub
    134.  
    135. #End Region
    136.  
    137. #Region "Private Functions"
    138.  
    139.     Private Sub WaitForPending()
    140.         Try
    141.             Dim bHolder(511) As Byte
    142.             Dim numIn As Integer
    143.             Dim dgRec As DataGramReceiver
    144.             Dim bTrans() As Byte
    145.  
    146.  
    147.             numIn = locUDP.Receive(bHolder, 512, Net.Sockets.SocketFlags.None)
    148.             If numIn > 0 Then
    149.                 If bHolder(0) <> whoAmI AndAlso bHolder(1) = 0 OrElse bHolder(1) = 100 Then
    150.                     'Set the holder up to the right size.
    151.                     ReDim bTrans(numIn)
    152.                     Array.Copy(bHolder, 3, bTrans, 0, numIn - 3)
    153.                     dgRec = New DataGramReceiver(bHolder(0), bHolder(2), bTrans)
    154.                     Add(dgRec)
    155.                 Else
    156.                     myContext.Post(AddressOf TimedOut, Nothing)
    157.                 End If
    158.             End If
    159.         Catch ex As System.Net.Sockets.SocketException
    160.             'Not actually doing anything here now.
    161.         Catch ex As Exception
    162.             'Not doing anything here.
    163.         Finally
    164.             myContext.Post(AddressOf TimedOut, Nothing)
    165.         End Try
    166.     End Sub
    167.  
    168.     'Adds one DGR to the queue
    169.     Private Sub Add(ByVal dgR As DataGramReceiver)
    170.         'Set the monitor.
    171.         Threading.Monitor.Enter(Me)
    172.         Try
    173.             dgList.Enqueue(dgR) 'That's all that happens.
    174.         Finally
    175.             Threading.Monitor.Exit(Me)
    176.         End Try
    177.         myContext.Post(AddressOf GotData, Nothing)
    178.     End Sub
    179.  
    180.     'This is invoked by the other thread.
    181.     Private Sub GotData(ByVal state As Object)
    182.         RaiseEvent MessageIn()
    183.     End Sub
    184.  
    185.     Private Sub TimedOut(ByVal state As Object)
    186.         ThreadListen = New Threading.Thread(AddressOf WaitForPending)
    187.         ThreadListen.Start()
    188.     End Sub
    189.  
    190.  
    191. #End Region
    192.  
    193. End Class
    My usual boring signature: Nothing

  8. #8

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,102

    Re: [RESOLVED] Strange UDP Issue

    In addition, here's the DataGramReceiver class that I use with the UDP Class. If you are passing strings, like a chat program, then neither class will be particularly right for you, but if you are broadcasting data packets, then this will work with any kind of packet. For my purposes, I have a series of structures that I serialize into a byte array. The leading bytes are there such that a broadcast packet can be made specific.

    These two classes are not truly generic, since I wrote them for a very specific purpose, but they may be of use to somebody.

    vb Code:
    1. 'A class that holds a pretty generic datagram. This is nothing but a type and an array of bytes.
    2. Public Class DataGramReceiver
    3.     Private mOrigin As Integer
    4.     Private mType As Integer
    5.     Private mBytes() As Byte
    6.  
    7. #Region "Constructors"
    8.  
    9.     Public Sub New(ByVal origin As Integer, ByVal typ As Integer, ByVal byt() As Byte)
    10.  
    11.         mOrigin = origin
    12.         mType = typ
    13.         'Size it.
    14.         ReDim mBytes(byt.GetUpperBound(0))
    15.         'Copy it. Don't want a shallow copy of byt() in mBytes.
    16.         Array.Copy(byt, mBytes, byt.Length)
    17.     End Sub
    18.  
    19.     Public Sub New(ByVal dgR As DataGramReceiver)
    20.  
    21.         mOrigin = dgR.mOrigin
    22.         mType = dgR.mType
    23.         ReDim mBytes(dgR.MaxBytes)
    24.         Array.Copy(dgR.GetByteArray, mBytes, dgR.MaxBytes + 1)
    25.     End Sub
    26.  
    27.     'A default constructor that sets the datagram into a known good, but empty state.
    28.     Public Sub New()
    29.         mOrigin = 0
    30.         mType = -1
    31.         ReDim mBytes(0)
    32.         mBytes(0) = 0
    33.     End Sub
    34.  
    35. #End Region
    36.  
    37. #Region "Properties"
    38.  
    39.     Public ReadOnly Property Type() As Integer
    40.         Get
    41.             Return mType
    42.         End Get
    43.     End Property
    44.  
    45.     Public ReadOnly Property MaxBytes() As Integer
    46.         Get
    47.             Return mBytes.GetUpperBound(0)
    48.         End Get
    49.     End Property
    50.  
    51.     'This returns the byte at the specified index, or -1 if the index is out of range.
    52.     Public ReadOnly Property GetByte(ByVal index As Integer) As Byte
    53.         Get
    54.             If index > 0 AndAlso index < mBytes.GetUpperBound(0) Then
    55.                 Return mBytes(index)
    56.             Else
    57.                 Return 0
    58.             End If
    59.         End Get
    60.     End Property
    61.  
    62.     Public ReadOnly Property GetByteArray() As Byte()
    63.         Get
    64.             Return mBytes
    65.         End Get
    66.     End Property
    67.  
    68.     Public ReadOnly Property Origin() As Integer
    69.         Get
    70.             Return mOrigin
    71.         End Get
    72.     End Property
    73.  
    74. #End Region
    75. End Class
    My usual boring signature: Nothing

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width