This article was written in the course of implementing a simple method for a Windows service to communicate status information to a front-end administration application. This article was written using the excellent article UDP send and receive using threads in VB.NET by Kumudu Gunasekara for inspiration and 'spiritual guidance', as well as some nifty ideas. Kumudu's article is definitely worth reading.
I decided to implement a simple UDP client/server system for simple communication between my Windows service and the administration program for a variety of reasons:
- UDP is lightweight and fast compared to other protocols, such as TCP. This was important to me, as my Windows service needed to maintain high performance.
- UDP is connectionless, which means that it can fire off a message and immediately free server-side network resources. Incidentally, this also makes UDP one of the easiest protocols to write a client/server application for.
- UDP preserves message boundaries transmitting entire messages at once.
Some of the drawbacks of UDP are:
- UDP does not guarantee delivery or provide for acknowledgment of receipt of messages. In this instance, that was perfectly acceptable since I was simply passing the status data from the Windows service to the UI layer.
- UDP has practical limits on how much data can be sent in a single message - usually limited to 1500 Bytes per message or less. Again, this limitation worked well given my scenario - I would normally not be passing more than 100 bytes per message.
Using the code
The code is pretty straightforward and well documented. There are two components:
Tiny.UDP.TinyClient. They are .NET 1.1 components, so they can be added to your Toolbox and dropped right in your Windows Forms; or you can reference them (as I did in the sample applications) by creating objects in the code. The code to create a server looks like this:
Dim server As New TinyServer
server.Protocol = ProtocolType.Udp
server.ClientAddress = IPAddress.Parse("127.0.0.1")
server.ClientPort = 8088
server.Encode = EncodingType.ASCII
All we're doing here is:
- Creating the
- Setting the communications protocol (currently only UDP is supported), client IP address and client port.
- And finally, setting the message encoding type (ASCII, UNICODE, UTF7, UTF8 and all others are supported).
Next we set up the
Dim WithEvents client As TinyClient
client = New TinyClient
client.ClientPort = 8088
client.Encode = EncodingType.ASCII
client.Protocol = ProtocolType.Udp
Again, it's a simple matter to set up a client:
- Define and create a new
TinyClient object. Notice it is declared
- Set the inbound client port number.
- Set the encoding type and protocol to match the
TinyServer that we created.
That's a simple UDP client and server with just 10 lines of code! (Not counting comments of course.) Finally, we start the client running with the
and send messages from the server with the
server.SendMessage ("This is a UDP Message sent by TinyServer.")
TinyClient object was declared
WithEvents because it implements two events:
BeforeReceive is fired immediately before receiving data off the wire.
AfterReceive is fired immediately after the client finishes receiving data. You can set up your own subroutines to handle these events as you see fit. Other events may be added later; but for now I decided those two were the most important to get implemented.
How does it work?
If you look into the code, you will see that the
TinyServer simply creates a
System.Net.Sockets.UdpClient, opens a connection, and fires off your message when you call the
SendMessage() method. Very simple. The client is where the fun begins.
On the client side, the
Start() method actually fires off a worker thread that in turn starts its own
System.Net.Sockets.UdpClient and waits for data. We set it off on a separate worker thread because the
Receive() method blocks the current thread until it completes - and we don't need it locking up our UI. Once a message is received by the worker thread, the thread exits immediately, so we start a new worker thread to continue waiting for the next message.
Because these events are fired from the worker thread, you'll notice in the sample program that I check
Me.InvokeRequired on the client form before I update it. This is necessary because of the nature of multi-threaded apps; never update the UI from a worker thread, always check
InvokeRequired and use
Invoke if it is
Finally, when we finish, fire the
client.Stop() method to stop the worker thread and cleanly dispose off the
Points of interest
As mentioned previously, this works best on a local machine or on locally networked computers; for instance, when communicating non-critical information between layers of a locally installed application. It also has the potential for chat applications, which I'm sure has been done to death by now :) My main goal in designing this was to create a reusable, lightweight set of components that could be used to allow Windows services to easily communicate with the front-end client applications without jumping through hoops.
- June 8th, 2005 - version 0.8.