A Simple UDP Application





A Simple UDP Application

As discussed in Chapter 3, “C# Network Programming Classes,” UDP is a connectionless protocol. Therefore, the programmer must do only two things to make a server application ready to send or receive UDP packets:

  • Create a Socket object

  • Bind the socket to a local IPEndPoint

After these two actions are taken, the socket can be used to either accept incoming UDP packets on the IPEndPoint, or send outgoing UDP packets to any other device on the network. All of the TCP connection requirements are unnecessary with UDP.

Note 

For client UDP applications that do not need to receive UDP packets on a specific UDP port, you do not have to bind the socket to a specific IPEndPoint—just create the Socket object and send data!

Because there is no connection between remote hosts, the UDP application cannot use the standard Send() and Receive() Socket methods. Instead, two new methods must be used, SendTo() and ReceiveFrom().

SendTo() The SendTo() method specifies the data to send and the IPEndPoint of the destination machine. There are several versions of this method that can be used, based on your requirements:

SendTo(byte[] data, EndPoint Remote)

This simple version of the SendTo() method sends a byte array data to the EndPoint specified by the variable Remote. A slightly more complex version is as follows:

SendTo(byte[] data, SocketFlags Flags, EndPoint Remote)

This version allows you to include a SocketFlags object Flags, which specifies any special UDP socket options to use. Use the following version of SendTo()to specify the number of bytes from the byte array to send:

SendTo(byte[data], int Size, SocketFlags Flags, EndPoint Remote)

The last version of the SendTo() method, with which you can specify a specific offset within the byte array to start sending data, is as follows:

SendTo(byte[] data, int Offset, int Size, SocketFlags Flags, EndPoint Remote)

ReceiveFrom() The ReceiveFrom() method has the same formats as the SendTo() method, with one important difference: the way the EndPoint object is declared. The basic ReceiveFrom() method is defined as follows:

ReceiveFrom(byte[] data, ref EndPoint Remote)

As usual, a byte array is defined to accept the received data.

What’s really of interest here is the second parameter, ref EndPoint. Instead of passing an EndPoint object, you must pass the reference to an EndPoint object. Although using references to variables is common in C and C++ programs, the structure seen here is not all that common in C# programs. The reference refers to the memory location where the variable is stored, not the value of the variable. The ReceiveFrom() method will place the EndPoint information from the remote device into the EndPoint object memory area you reference.

Both SendTo() and ReceiveFrom() are included in the simple UDP server and client examples in the following sections. These examples demonstrate the basics of connectionless communication with C# sockets.

The UDP Server

Although UDP applications aren’t really servers or clients by strict definition, I will call this next application a UDP server for the sake of simplicity. It creates a Socket object and binds it to a set IPEndPoint object so it can wait for incoming packets:

IPEndPoint ipep = new IPEndPoint(IPAddress.Any,
               9050);
Socket newsock = Socket(AddressFamily.InterNetwork,
       SocketType.Dgram, ProtocolType.Udp);
newsock.Bind(ipep);

For connectionless communications, you must specify the Dgram SocketType, along with the Udp ProtocolType. Remember, if your application does not need to receive UDP data on a specific UDP port, you do not have to bind the socket to a specific IPEndPoint. However, if you do need to listen to specific port, such as for servers, you must use the Bind() method.

Listing 6.1, the SimpleUdpSrvr.cs program, demonstrates the basic components of a simple UDP server by binding a dedicated socket on the server for other UDP clients to connect to.

Listing 6.1: The SimpleUdpSrvr.cs program
Start example
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class SimpleUdpSrvr
{
  public static void Main()
  {
   int recv;
   byte[] data = new byte[1024];
   IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
   Socket newsock = new Socket(AddressFamily.InterNetwork,
           SocketType.Dgram, ProtocolType.Udp);
   newsock.Bind(ipep);
   Console.WriteLine("Waiting for a client...");
   IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
   EndPoint Remote = (EndPoint)(sender);
   recv = newsock.ReceiveFrom(data, ref Remote);
   Console.WriteLine("Message received from {0}:", Remote.ToString());
   Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
   string welcome = "Welcome to my test server";
   data = Encoding.ASCII.GetBytes(welcome);
   newsock.SendTo(data, data.Length, SocketFlags.None, Remote);
   while(true)
   {
     data = new byte[1024];
     recv = newsock.ReceiveFrom(data, ref Remote);
   
     Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
     newsock.SendTo(data, recv, SocketFlags.None, Remote);
   }
  }
}
End example

As mentioned, for the UDP server program to accept incoming UDP messages, it must be bound to a specific UDP port on the local system. This is accomplished by creating an IPEndPoint object using the appropriate local IP address (or as shown here, the IPAddress.Any address to use any network interface on the system), and the appropriate UDP port:

IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
Socket newsock = new Socket(AddressFamily.InterNetwork,
           SocketType.Dgram, ProtocolType.Udp);
newsock.Bind(ipep);

The sample UDP server illustrated in the example will accept any incoming UDP packet on port 9050 from the network.

Note 

There is not an established connection between hosts, so UDP is not picky about where the packet comes from (unlike TCP). However, because of this feature of UDP, when communicating with multiple UDP clients you must be careful to check the transmission point of the received packets.

When creating your sample UDP server, be careful that you don’t choose a UDP port that is already in use by another application on your machine. You can monitor the arrangement of applications that are listening on particular UDP ports by using the netstat command at a command prompt. Here’s an example of the results:

C:\>netstat -a
Active Connections
 Proto Local Address     Foreign Address    State
 TCP  abednego:epmap     0.0.0.0:0       LISTENING
 TCP  abednego:microsoft-ds 0.0.0.0:0       LISTENING
 TCP  abednego:1025     0.0.0.0:0       LISTENING
 TCP  abednego:1034     0.0.0.0:0       LISTENING
 TCP  abednego:5000     0.0.0.0:0       LISTENING
 TCP  abednego:12174     0.0.0.0:0       LISTENING
 TCP  abednego:38292     0.0.0.0:0       LISTENING
 TCP  abednego:1026     0.0.0.0:0       LISTENING
 TCP  abednego:netbios-ssn  0.0.0.0:0       LISTENING
 TCP  abednego:11133     0.0.0.0:0       LISTENING
 UDP  abednego:epmap     *:*
 UDP  abednego:microsoft-ds *:*
 UDP  abednego:isakmp    *:*
 UDP  abednego:1027     *:*
 UDP  abednego:1035     *:*
 UDP  abednego:9050     *:*
 UDP  abednego:38037     *:*
 UDP  abednego:38293     *:*
 UDP  abednego:ntp      *:*
 UDP  abednego:1900     *:*
 UDP  abednego:ntp      *:*
 UDP  abednego:netbios-ns  *:*
 UDP  abednego:netbios-dgm  *:*
 UDP  abednego:1036     *:*
 UDP  abednego:1900     *:*
 UDP  abednego:12564     *:*
C:\>

The netstat -a command displays all the active TCP and UDP sessions on the system. Depending on how many network applications are configured on your system, you might see lots of output here. The first column shows the protocol type, and the second column gives you the hostname or IP address and the port number. In the third column, you get the IP hostname or address and the port number of the remote device, if the port is connected to a remote device (for TCP). The last column shows the TCP state of TCP connections.

You can select any UDP port that is not shown as being active in your netstat list. Once the SimpleUdpSrvr program is active, you will see an entry for the UDP port you selected, as shown in the preceding netstat output with the 9050 port number:

UDP  abednego:9050     *.*

Similar to the TCP client/server model, remote devices communicating with UDP must agree on a system to use for sending and receiving data. If both client and server are waiting for data at the same time, both devices will block and not work. To make this arrangement, the SimpleUdpSrvr program follows the following protocol:

  • Wait to receive a message from a client

  • Send a welcome banner back to the client

  • Wait for additional client messages and send them back

These functions are accomplished using the ReceiveFrom() and SendTo() methods of the Socket class.

Because connectionless sockets do not establish a connection, each SendTo() method must include the remote device EndPoint information. It is imperative that you have this information available from the received message. To obtain it, a blank EndPoint object is created and referenced in the ReceiveFrom() method:

IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)(sender);
recv = newsock.ReceiveFrom(data, ref Remote);

The Remote object contains the IP information for the remote device that sent the message to the server. This information identifies the incoming data and is also used if the server wants to return a message to the client:

string welcome = "Welcome to my test server";
data = Encoding.ASCII.GetBytes(welcome);
newsock.SendTo(data, data.Length, SocketFlags.None, Remote);
Warning 

As is true for the TCP server in Chapter 5, “Connection-Oriented Sockets,” you must always reset the receive data buffer to its full size before the ReceiveFrom() method call occurs, or the Length property will reflect the length of the previously received message. This could cause some unexpected results.

Unlike the TCP server discussed in Chapter 5, the UDP server cannot be tested without an appropriate UDP client. The next section describes how to create this client.

A UDP Client

The UDP client program is similar to its partner server program.

Because the client does not need to wait on a specific UDP port for incoming data, it does not use the Bind() method. Instead, it employs a random UDP port assigned by the system when the data is sent, and it uses the same port to receive return messages. If you are in a production environment, you might want to specify a set UDP port for the client as well, so that both the server and client programs use the same port numbers.

Warning 

Be careful if you are using the UDP server and client programs on the same machine. You cannot Bind() the same UDP port number for both programs, or an error will occur. Only one application can bind to a specific port number at a time. Either select a different port number, or let the system choose a random port for the client.

Listing 6.2 shows the SimpleUdpCLient.cs program that demonstrates the fundamentals of a UDP setup.

Listing 6.2: The SimpleUdpClient.cs program
Start example
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class SimpleUdpClient
{
  public static void Main()
  {
   byte[] data = new byte[1024];
   string input, stringData;
   IPEndPoint ipep = new IPEndPoint(
           IPAddress.Parse("127.0.0.1"), 9050);
   Socket server = new Socket(AddressFamily.InterNetwork,
           SocketType.Dgram, ProtocolType.Udp);
   string welcome = "Hello, are you there?";
   data = Encoding.ASCII.GetBytes(welcome);
   server.SendTo(data, data.Length, SocketFlags.None, ipep);
   IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
   EndPoint Remote = (EndPoint)sender;
   data = new byte[1024];
   int recv = server.ReceiveFrom(data, ref Remote);
   Console.WriteLine("Message received from {0}:", Remote.ToString());
   Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
   while(true)
   {
     input = Console.ReadLine();
     if (input == "exit")
      break;
     server.SendTo(Encoding.ASCII.GetBytes(input), Remote);
     data = new byte[1024];
     recv = server.ReceiveFrom(data, ref Remote);
     stringData = Encoding.ASCII.GetString(data, 0, recv);
     Console.WriteLine(stringData);
   }
   Console.WriteLine("Stopping client");
   server.Close();
  }
}
End example

The UDP client program first defines an IPEndPoint to which the UDP server device will send packets. If you are running the SimpleUdpSrvr program on a remote device, you must enter the appropriate IP address and UDP port number information in the IPEndPoint definition:

IPEndPoint ipep = new IPEndPoint(
           IPAddress.Parse("127.0.0.1"), 9050);

The client program sends a quick message to the server machine to introduce itself, then waits for the welcome banner message to be sent back. Because it does not need to accept UDP messages on a specific port number, the client does not bind the Socket object. It will receive the return UDP message on the same port from which it sent the original message.

The SimpleUdpClient program reads the console input and waits for the phrase exit to appear before leaving the while loop. Once the loop is exited, the socket is closed.

Testing the Client and Server Programs

Start the SimpleUdpSrvr program on the assigned server machine you will use for your testing. When the server program starts, it displays a short message:

C:\>SimpleUdpSrvr
Waiting for a client...

Nothing too fancy here. After the server is started, you can run the SimpleUdpClient program in either a separate command-prompt window on the same machine, or on another machine on the network. (If you use a remote machine, remember to change the 127.0.0.1 IP address to the address of the server machine.)

SimpleUdpClient immediately sends a message to the server, which in turn should send its welcome banner back. On the client, it looks like this:

C:\>SimpleUdpClient
Message received from 127.0.0.1:9050:
Welcome to my test server

Notice that the EndPoint object of the remote server indicates that it is indeed sending data out from the same UDP port to which it was bound, 9050.

On the server side, you should see the connection message sent by the client:

C:\>SimpleUdpSrvr
Waiting for a client...
Message received from 127.0.0.1:1340:
Hello, are you there?

Notice that the client, because it was not bound to a specific UDP port, selected a free UDP port to use for the communication. Once the opening protocol has been completed, you can type messages into the client console and see them sent to the server and displayed, then echoed back to the client and displayed.

If you are connecting to the server from a remote client, you can use the WinDump and Analyzer programs to monitor the network traffic generated by the applications. Figure shows a sample Analyzer output window containing output from the simple client program.

Click To expand
Figure: Sample Analyzer output from the SimpleUdpClient program

As can be seen from the network packets, each UDP message is sent in a single packet to the server, and each UDP message echoed back is sent as a single packet to the client. This is characteristic of how UDP preserves the message boundaries.

Using Connect() in a UDP Client Example

You may be wondering why the UDP client application’s ReceiveFrom() and SendTo() methods are so complicated when you are only receiving and sending data to a single UDP server. Well, they don’t have to be. As mentioned, the UDP methods are designed to allow the programmer to send UDP packets to any host on the network at any time. Because no prior connection is required for UDP, you must specify the destination host in each SendTo() and ReceiveFrom() method used in the program. If you’re planning to send and receive data from only one host, you can take a shortcut.

After the UDP socket is created, you can use the standard Connect() method normally employed in TCP programs to specify the remote UDP server. With that in place, you can then use the Receive() and Send() methods to transfer data to the remote host. The communication still uses UDP packets, and you get to do a lot less work! This technique is demonstrated in the sample UDP client program in Listing 6.3.

Listing 6.3: The OddUdpClient.cs program
Start example
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class OddUdpClient
{
  public static void Main()
  {
   byte[] data = new byte[1024];
   string input, stringData;
   IPEndPoint ipep = new IPEndPoint(
           IPAddress.Parse("127.0.0.1"), 9050);
   Socket server = new Socket(AddressFamily.InterNetwork,
           SocketType.Dgram, ProtocolType.Udp);
   server.Connect(ipep);
   string welcome = "Hello, are you there?";
   data = Encoding.ASCII.GetBytes(welcome);
   server.Send(data);
   data = new byte[1024];
   int recv = server.Receive(data);
   Console.WriteLine("Message received from {0}:", ipep.ToString());
   Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
   while(true)
   {
     input = Console.ReadLine();
     if (input == "exit")
      break;
     server.Send(Encoding.ASCII.GetBytes(input));
     data = new byte[1024];
     recv = server.Receive(data);
     stringData = Encoding.ASCII.GetString(data, 0, recv);
     Console.WriteLine(stringData);
   }
   Console.WriteLine("Stopping client");
   server.Close();
  }
}
End example

The OddUdpClient program behaves exactly like the SimpleUdpClient program. The only difference is that once the UDP socket is created, it is connected to a specific IPEndPoint:

IPEndPoint ipep = new IPEndPoint(
           IPAddress.Parse("127.0.0.1"), 9050);
Socket server = new Socket(AddressFamily.InterNetwork,
           SocketType.Dgram, ProtocolType.Udp);
server.Connect(ipep);

In this case, the Connect() method does not really do what it says. Because the socket is defined as a UDP datagram socket, no actual connection is made, but the socket information is "set" to the IPEndPoint object. All calls to the Send() and Receive() methods are now automatically referenced to the IPEndPoint object. You do not have to use the clunky SendTo() and Receive() methods to handle the data.

You can test the OddUdpClient program by starting the SimpleUdpSrvr as you normally would and using the OddUdpClient program to send messages to it. Things should work just as they did for the SimpleUdpClient program. You can even watch the traffic with WinDump or Analyzer if you don’t believe that it’s really sending UDP packets! Also, note that each message is sent as a single packet to the server, preserving all message boundaries, just as in the SimpleUdpClient.cs program.


 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows