Results 1 to 8 of 8

Thread: A Generalized UDP Class

  1. #1

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

    A Generalized UDP Class

    I have attached here a generalized UDP class. This has been tested, and is working, though there are a few notes that I will make in following posts. There are actually two classes here, with the EventArgument class in the second snippet.

    Code:
    Public Class UDPCommon
        Implements IDisposable
    
        'The UDP listening components.
        Private WithEvents locUDP As System.Net.Sockets.Socket
        Private WithEvents locUDPOut As System.Net.Sockets.UdpClient
        Private ThreadListen As Threading.Thread
        Private timeCount As Integer
        Private mTargetEndPoint As System.Net.IPEndPoint
        Private stopListening As Boolean
        Private Const MAX_PACKET_SIZE = 4096
    
        'The dataQueue
        Private dgList As Queue(Of Byte())
    
        'The result
        Private disposedValue As Boolean = False        ' To detect redundant calls
    
        'Synchronization.
        Private Shared myContext As System.Threading.SynchronizationContext
    
    #Region "Constructors and Destructors"
    
        Public Sub New(ByVal inPort As Integer)
            'Set up the queue.
            myPort = inPort
            mMyIPAddress = Net.IPAddress.Any
            mTargetIP = Net.IPAddress.Broadcast
            mTargetEndPoint = New System.Net.IPEndPoint(mTargetIP, myPort)
    
            dgList = New Queue(Of Byte())
            locUDPOut = New Net.Sockets.UdpClient()
            locUDPOut.Client.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, True)
            locUDP = New Net.Sockets.Socket(Net.Sockets.AddressFamily.InterNetwork, Net.Sockets.SocketType.Dgram, Net.Sockets.ProtocolType.Udp)
            locUDP.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, True)
            locUDP.Bind((New Net.IPEndPoint(mMyIPAddress, myPort)))
            'locUDP.ReceiveTimeout = 3000
    
            locUDPOut.EnableBroadcast = True
    
    
    
            myContext = System.Threading.SynchronizationContext.Current
    
            'Start listening.
            ThreadListen = New Threading.Thread(AddressOf WaitForPending)
            ThreadListen.Name = "Listening Thread"
            ThreadListen.IsBackground = True 'Program termination will terminate the thread.
            ThreadListen.Start()
        End Sub
    
        ' IDisposable
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    ThreadListen.Abort()
                    locUDP.Close()
                    locUDPOut.Close()
                End If
    
                ' TODO: free shared unmanaged resources
            End If
            Me.disposedValue = True
        End Sub
    
    #Region " IDisposable Support "
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
    #End Region
    
    #End Region
    
    #Region "Properties and Events"
    
        Public Event MessageIn(ByVal sender As Object, ByVal e As UDPEventArgs)
    
        Public ReadOnly Property MessageCount() As Integer
            Get
                Return dgList.Count
            End Get
        End Property
    
        Public ReadOnly Property NextMessage() As Byte()
            Get
                Return dgList.Dequeue
            End Get
        End Property
    
        Private myPort As Integer
        Public ReadOnly Property Port() As Integer
            Get
                Return myPort
            End Get
        End Property
    
        Private mMyIPAddress As Net.IPAddress
        Public Property SourceIP() As Net.IPAddress
            Get
                Return mMyIPAddress
            End Get
            Set(ByVal value As Net.IPAddress)
                mMyIPAddress = value
            End Set
        End Property
    
        Private mTargetIP As Net.IPAddress
        Public Property TargetIP() As Net.IPAddress
            Get
                Return mTargetIP
            End Get
            Set(ByVal value As Net.IPAddress)
                mTargetIP = value
                mTargetEndPoint = New System.Net.IPEndPoint(mTargetIP, myPort)
            End Set
        End Property
    
    #End Region
    
    #Region "Public Functions"
    
        'This will get the next DGR from the queue.
        Public Function GetNext() As Byte()
            Dim dgRet As Byte() = Nothing
    
            Threading.Monitor.Enter(Me)
            Try
                If dgList.Count > 0 Then
                    dgRet = dgList.Dequeue
                End If
            Catch ex As Exception
                System.Windows.Forms.MessageBox.Show(ex.Message, "Error Getting Array", Windows.Forms.MessageBoxButtons.OK, Windows.Forms.MessageBoxIcon.Error)
            Finally
                Threading.Monitor.Exit(Me)
            End Try
    
            'It may be the default, or it may not, either way it is a good local copy in the calling thread, return it.
            Return dgRet
    
        End Function
    
        'Empties the queue
        Public Sub Flush()
            Threading.Monitor.Enter(Me)
            Try
                dgList.Clear()
            Finally
                Threading.Monitor.Exit(Me)
            End Try
        End Sub
    
        Public Sub SendMessage(ByVal btArr() As Byte)
            locUDPOut.Send(btArr, btArr.Length, mTargetEndPoint)
        End Sub
    
        'Public Sub StopReceiving()
        '    stopListening = True
        'End Sub
    
    #End Region
    
    #Region "Private Functions"
    
        Private Sub WaitForPending()
            Do
                Try
                    Dim bHolder(MAX_PACKET_SIZE - 1) As Byte
                    Dim numIn As Integer
    
                    'The thread will simply block on this until a message is received.
                    'This means that only aborting the thread is capable of ending this.
                    numIn = locUDP.Receive(bHolder, MAX_PACKET_SIZE, Net.Sockets.SocketFlags.None)
                    If numIn > 0 Then
                        Add(bHolder)
                    End If
    
                Catch ex As System.Net.Sockets.SocketException
                    'Not actually doing anything here now.
                Catch ex As Exception
                    'Not doing anything here.
                End Try
            Loop While Not stopListening
        End Sub
    
        'Adds one array to the queue safely.
        Private Sub Add(ByVal dgR As Byte())
            'Set the monitor.
            Threading.Monitor.Enter(Me)
            Try
                dgList.Enqueue(dgR) 'That's all that happens.
            Finally
                Threading.Monitor.Exit(Me)
            End Try
            Dim e As New UDPEventArgs
            e.Payload = dgR
            myContext.Post(AddressOf GotData, e)
        End Sub
    
        'This is invoked by the other thread.
        Private Sub GotData(ByVal state As Object)
            RaiseEvent MessageIn(Me, DirectCast(state, UDPEventArgs))
        End Sub
    
    #End Region
    
    End Class
    Code:
    Public Class UDPEventArgs
        Inherits EventArgs
    
        Public Payload As Byte()
    End Class
    My usual boring signature: Nothing

  2. #2

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

    Re: A Generalized UDP Class

    There are a couple items to note about this class. By default, it is broadcasting, and it is listening on IPAddress.Any. The broadcasting is something that you might want to restrict before using, unless you actually want broadcasting, but listening on Any is probably ok for most uses.

    Another note is that the packet size is set by a constant. I'm arbitrarily using 4096 as the packet size. This should cause no problems as long as your packets are smaller, but if your packets are larger, then you will need to change this to a larger value or else you will have the ends of all of your packets lopped off. There is a maximum packet size for UDP, but it is dependent on individual networks, and can be anything from HUGE to tiny, partially depending on the steps taken along the way.

    There is also some code in the class that does effectively nothing, much of which is commented out. This all has to do with a debate I was having with myself over how best to implement this. The code in the WaitForPending sub acts in a loop, which will keep going as long as a certain boolean is clear. This suggests that you could interrupt the loop at any time, which is the purpose behind the StopReceiving method. But Socket.Receive will block until a message has been received. Therefore, the loop is only going to happen if a packet is received, regardless of the setting of that boolean. If there are no packets coming in, then StopReceiving will do absolutely nothing. Therefore, I commented out the whole method, lest somebody think it actually does something.

    I left that in there, because there is a way that it CAN do something. In the constructor, there is a commented out setting for ReceiveTimeout. If that comment is removed, then Socket.Receive will timeout after the designated interval, which means that the loop will keep running, and the StopReceiving method is meaningful. However, the mechanism used for the Timeout is that an exception is raised. Exceptions are a really slow mechanism in .NET. To have a protocol as fast as UDP raising exceptions every second or two would take the socket away from listening for a fair amount of time, and for what gain? The only possible use for this would be to allow the user of the code to stop the listener. No other possible good can come of the timeout. But there is no reasonable means to restart the listener. It actually makes more sense to dispose of the class, which will abort the thread, and create a new class rather than stopping and starting a listener thread. Therefore, I commented out the entire receive timeout functionality, as well as the StopReceiving method. If you want to stop receiving, dispose of the class. You can always make a new one.

    To test the class, I created two projects and added the class to each (I have it as a dll). I then ran both projects simultaneously. This line:
    Code:
    locUDPOut.Client.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, True)
    allows multiple projects to listen to the same socket. Without the line, only one socket can bind to the address at a time.

    In one test, I set the target IP as IPAddress.LoopBack. This had some quirks to it. With both projects running, one would send out strings, receive its own message (it is listening to Any, so it is listening to the Loopback), and the other program would receive the message. For the other project, it would send the string, and the first project would see it, but the second project wouldn't see it's own string. Therefore, one sent and received its own string, while the other could send, but could only receive strings from the first program. It appeared to be that the one that started second got its own outgoing mail, and the one that started first didn't get its own outgoing mail.

    In the second test, I changed the target to Broadcast. Using that endpoint, each side sent and received all messages from themselves and the other project.

    All messages are byte arrays, so it is up to the user to create the byte arrays.

    An event is raised whenever a message is received, and that event contains the incoming byte array. The byte array is also added to a queue, and you can use the GetNextMessage method to get the next byte array on the queue. If you don't use the queue, it will keep filling up, so you might want to alter the code to flush the queue, or remove it altogether if all you want to do is deal with the incoming events. However, keep in mind that UDP is a really fast protocol, so if there is lots of communication going on, messages might well come in faster than you can reasonably handle them, which is why the queue is there.
    My usual boring signature: Nothing

  3. #3
    Junior Member
    Join Date
    Apr 2010
    Posts
    20

    Re: A Generalized UDP Class

    thanks for the code. i am trying to use it for my rcon project and its gonna take me quite some time but if i am successful, i will post about it in my earlier thread. Anyways, i know you said you dont know of any other way to wait for the fragmented packets than multi-threading but i was just curious why couldnt you use the function...

    Also, could use a bit more comments in the code told ya im a noob.. but i think i am finally understanding you codes now

  4. #4

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

    Re: A Generalized UDP Class

    What function?
    My usual boring signature: Nothing

  5. #5
    Junior Member
    Join Date
    Apr 2010
    Posts
    20

    Re: A Generalized UDP Class

    a FUNCTION as in

    function func()

    end function

    Well, anyway .. i tried to follow what you did and it took me a long painful week to get to it but it didnt work out for me

    Fortunately, somebody @msdn had a very simple solution to this problem so its all good now. Thanks very much for the help.

  6. #6
    I don't do your homework! opus's Avatar
    Join Date
    Jun 2000
    Location
    Good Old Europe
    Posts
    3,863

    Re: A Generalized UDP Class

    Hi,
    I know this an old thread, however I have a question on the correct useage.
    As stated
    An event is raised whenever a message is received, and that event contains the incoming byte array.
    , so I'd like to make use of that. Where do I have to place a Sub that handles this event(like pasting the message-byte-array somewhere on my form)? Should such a Sub be within the UDPCommon Class or with in the class of the form?

    Nevermind, found it:

    The Routine must be in the Forms-Class, all I forgot was the AddHandler-line after instanciating a UDPCommon object. Me stupid, tried it with the ..Handles in the declaration-line of the event-routine.

    No its running, Dankeschön!!!
    Last edited by opus; Apr 7th, 2011 at 01:41 PM. Reason: Found it, ErrorCode 40!
    You're welcome to rate this post!
    If your problem is solved, please use the Mark thread as resolved button


    Wait, I'm too old to hurry!

  7. #7
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,480

    Re: A Generalized UDP Class

    thanks shaggy. i got it working now... if you remember i couldn't before, but you were right it was just a firewall problem. amazing what eating + sleeping can do

  8. #8
    Still learning kebo's Avatar
    Join Date
    Apr 2004
    Location
    Gardnerville,nv
    Posts
    3,758

    Re: A Generalized UDP Class

    This is a pretty nice class. One change that I made to make it more useful was to include the number of returned bytes in the UDPEventArgs class. That also meant passing the number to the Add method as well. After that change though it made it easier to run the byte array through the Encoding.ASCII.GetString(byte(),start,length) method.
    Nice work Shaggy
    Process control doesn't give you good quality, it gives you consistent quality.
    Good quality comes from consistently doing the right things.

    Vague general questions have vague general answers.
    A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.

    ______________________________
    Last edited by kebo : Now. Reason: superfluous typo's

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