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.