This article is on UDP socket programming. You will find lots of articles and blogs on TCP sockets but not many informative ones on UDP sockets. We use UDP when we have fewer resources, support more clients or in fire and forget scenarios . UDP sockets are pretty simple, but writing efficient UDP socket code isn't. In this article, I will discuss the advantages and disadvantages of using UDP. You will also see a sample application code.
UDP or TCP?
TCP maintains a session, and guarantees reliability. UDP provides no such features. With UDP, no connection is maintained. If a packet is lost using UDP, the application must detect and remedy the situation.
Using UDP a single packet can be sent to multiple machines at once using multicast and broadcast. Multicast can be used to send a message even if we do not know the IP address of the target machine.
Since TCP guarantees that data will be processed by the server in the order it was sent, packet loss on a TCP connection stops the processing of any data until the lost packet is received successfully. For some applications this behavior is not acceptable, but others can proceed without the missing packet. For example, the loss of one packet in a broadcast video should not cause a delay because the application should just play the next frame.
When packet-loss is detected, TCP slows down the rate of outgoing info on a connection. This can result in slow transmission rates on networks with high packet loss.
On the other hand, TCP has distinct advantages in simplicity of use and implementation. Many security threats are resolved in the TCP stack. Also, firewalls cannot easily identify and manage UDP traffic.
Lets go through the code implementing a simple UDP receive and send.
-------------------------------------------------------------------------------
Socket requestSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//Assign the any IP of the machine and listen on port number 1000
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 1000);
//Bind this address to the server
serverSocket.Bind(ipeServer);
IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);
//The epSender identifies the incoming clients
EndPoint epSender = (EndPoint) ipeSender;
//Start receiving data
serverSocket.BeginReceiveFrom (byteData, 0, byteData.Length,
SocketFlags.None, ref epSender,
new AsyncCallback(OnReceive), byteData);
-------------------------------------------------------------------------
With IPAddress.Any, we specify that the server can accept client requests coming from any IP. To use any particular IP, we can use IPAddress.Parse ("IP here"). The Bind function then binds the serverSocket to the specified IP address and Port (this port is the port on which the server is listening). The epSender gives the endpoint of the client from which data is received. Note that epSender is passed as a reference to the BeginReceiveFrom method.
With BeginReceiveFrom, we start receiving async data. Here OnReceive method gets called when a UDP message is received. Look at the definition of OnReceive method below:
-------------------------------------------------------------------------------
void OnReceive(IAsyncResult result)
{
EndPoint remoteEndPoint = new IPEndPoint(0, 0);
try
{
int bytesRead = receiveSocket.EndReceiveFrom(result, ref remoteEndPoint);
byteData = (byte[])result.AsyncState;
if(bytesRead > 0)
{
//Start receiving data again
serverSocket.BeginReceiveFrom (byteData, 0, byteData.Length,
SocketFlags.None, ref epSender,
new AsyncCallback(OnReceive), byteData);
}
}
catch (SocketException e)
{
Console.WriteLine("Error: {0} {1}", e.ErrorCode, e.Message);
}
}
-------------------------------------------------------------------------------
We passed byteData as the last parameter of BeginReceiveFrom method. This byte array now becomes available as part of IAsyncResult. We can pass any object as a parameter in the BeginReceiveFrom method and retrieve it from IAsyncResult.
We can handle the retrieved byte array and use it for processing on the server side. Once all processing is done we can send either a multicast message or send a response to each individual client which sent a request.
Sending a multicast message:
-------------------------------------------------------------------------------
IPAddress multicastGroup = IPAddress.Parse("IP here");
const int ProtocolPort = 3001;
Socket sendSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
EndPoint sendEndPoint = new IPEndPoint(multicastGroup, ProtocolPort);
sendSocket.SendTo(buffer, bufferUsed, SocketFlags.None, sendEndPoint);
-------------------------------------------------------------------------------
Sending response to each individual client:
-------------------------------------------------------------------------------
Socket requestSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
EndPoint requestEndPointDestination = new IPEndPoint(((IPEndPoint)remoteEndPoint).Address, request.responsePort);
requestSocket.SendTo(byteData, byteData.length,
SocketFlags.None, requestEndPointDestination);
-------------------------------------------------------------------------------
If you notice when we send a reponse to an individual client, we are able to retrieve the IP address of the client and the port on which it is listening. We send the reponse on the same IP and Port. We can specify a different Port as required.
The above code demonstrated how to receive and send UDP messages. I would also like to highlight that we might face a lot of performance problems with UDP. Please keep in mind the below metioned points if performance is a criteria:
- Turn off firewall on the machine on which the UDP application is running.
- An async method called BeginSendTo is provided in sockets. Use this method
appropriately.
- SendTo method gives better performance.
- Thread spawning for various reasons eats up a lot of time. Avoid using threads where ever possible.
- Use appropriate buffer size.
No comments:
Post a Comment