Introduction
This article is about a scalable UDP socket server program. This program uses Async Socket methods to support communication with multiple clients. The server is designed in a way that it will isolate receive and send functionality. In this program there is also provision to process your data in individual threads. In any case the server will not stop listening. I have built the skeleton that will provide only the architecture where you can use your business logic for development.
Note:- This is a pattern using you can implement the more scalable UDP Server. In case you need more assistance please do write in comments section.
Background
Our client has real time devices fixed on properties for security. This device is capable of intruder detection and fire smoke detection. There are nearly 5000 devices present all over the country. The device contains a SIM card for the communication. It uses GPRS via the UDP communication protocol. These devices needs a communication rooter which provides understandable information for third party systems.
My requirement was to integrate my application with these devices. Due to the high number of clients I needed to develop a server which could manage communication concurrently with multiple devices. The devices need to be communicated to once it starts sending messages. The device starts communication only under a condition: if any of the sensors is activated and triggers the device.
Once the device gets triggered in any of the events, devices start sending signals to the server. The server’s responsibility is to respond to these requests and gather data from the device .Then assemble that information and produce the required output.
To develop this type of application, performance is very critical. The reason for criticality is that all communication to the server is on only one port. We have to manage each and every byte very carefully so it can serve as many devices as possible at a time. The pick time of device calling is morning 8 to 9. Each device has an individual IP and port which communicates on only one port of the server.
Process
To develop this application my starting requirement was to establish communication between devices and the server. This is not a difficult problem, I just need to configure the server with an open IP to communicate with the device. The data retrieved from the device is very critical and requires a CRC check. We need to maintain data information in the trace log for each device.
With this requirement my first thought was how many concurrent devices we can support. I did research on the communication mode. First I used the Winsock control. The reason for using Winsock is that I was able to send a response to the device. The problem with Winsock is that it is a very heavy object and in the case of a time out, the connection gets disconnected and Winsock sits idle and will not respond to the device. To resolve this problem I found out that I can dispose Winsock and then recreate it. That works but again the time constraint is there.
Then I did some research on the System.Net.Sockets.Socket class. Socket works fine with the device. The good news is Socket supports async communication. One of the advantages about UDP is that it never blocks sending and saves us multiple ports for communication on the server. The socket BeginRecieve() and BeginSendTo() methods are used to manage async calls with different devices.
The calls are multiple with the same device because of the data we need to get in multiple chunks. Multiple devices are calling at the same time so we need to keep track of each and every request and reply on the correct device. In case data does not arrive in the defined timeline we need to implement a retry strategy. I used four components in this project.
- GUI will display the on-going process events happening in the application.
- The Data Collector class holds data for processing.
- Data process class processes data.
- Utility functions.
This approach gives me isolation of send receive information and increases response time for the server.
Environment Diagram
The devices communicate via a SIM card to our Session Manager. Once the data is received into the session manager the data is passed to the processing unit. The processing unit contains the data collector and data process modules which has the functionality of handling the data. The session manager is an intelligent unit which is able to manage the traffic of incoming and outgoing data.
Using the code
The project is divided into four parts.
- UDP.Server
- UDP.Server.Utils
- UDP.ServerComponents
- UDP.ServerCore
Here are the details:
- UDP.Server: This is the starting point of the application. The work of the UDP.Server library is just a part of the View, the traffic, and traffic related events.
- UDP.ServerCore: This is the main core of the project. The functionality listed below should be present in this module:
- Manage send receive data packets of UDP.
- Act as a router for packets to correct data collector container.
- Once data is collected pass it to the proper process module.
- Clean up the data object once the data is processed.
- Send notification to the server UI.
- UDP.ServerComponents: Secure pass components contain the structure of the data which you need to process. Like an entity class you can create structure, class, or enumeration in this class. In these components there should not be any business logic present. They should only contain the structure. For example, email structure. If you want to send email, create an email structure class. Create an instance of the class and use it wherever you need.
- UDP.Server.Utils: As per the name I have used this project for all utilities. For FTP connectivity, CRC calculation, and other common functionality I am putting in this project.
In this project you will only get the skeleton of the project you need to implement your code as per your requirements.
The class SessionManager
is the core of the program. SessionManager
’s responsibilities are:
- Start and stop the server.
- Receive and send data on UDP.
- Start processing thread to process data.
- Determines data is passed to the correct data collector.
The class SessionManager
is used to manage communication related stuff. The Socket
gets initialized when the SessionManagers
object is instantiated.
private Socket _UdpSocket;
private void InitializeUdpSocket()
{
_UdpSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
}
Below code is for starting the listener. This function starts receiving UDP traffic. UpdateUI
is the callback method which will notify the GUI what data we have received.
public delegate void UpdateUIDelegate(object value);
public event UpdateUIDelegate UpdateUI;
public void StartListener()
{
_ReceiveByteData = new byte[1072];
_UdpSocket.Bind(new IPEndPoint(IPAddress.Any, Int32.Parse(
SPConfigVal.GetConfigValue(SecurePassConstants.ServerPort))));
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
_UdpSocket.BeginReceiveFrom(_ReceiveByteData, 0, _ReceiveByteData.Length,
SocketFlags.None, ref newClientEP, DoReceiveFrom, _UdpSocket);
if (UpdateUI != null)
{
UpdateUI(CommonFunctions.GetDateTime() + " Server Stated....");
}
}
The DoReceivedForm
method is used as async and receives traffic from multiple Devices. Once data is received it transfers data to the collection container. Once data is collected the class returns data for the next request and sends the request as soon as possible. The reason behind this fast process is we need to keep the server ready for new incoming requests.
private void DoReceiveFrom(IAsyncResult ar)
{
try
{
Socket recvSock = (Socket)ar.AsyncState;
EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
int msgLen = recvSock.EndReceiveFrom(ar, ref clientEP);
byte[] objbuffer = new byte[msgLen];
Array.Copy(_ReceiveByteData, objbuffer, msgLen);
_ReceiveByteData = null;
if (UpdateUI != null)
{
UpdateUI(objbuffer);
}
_SendByteData = ProcessDeviceData(objbuffer, clientEP);
SendData(clientEP, _SendByteData);
}
The request accepted by the device is a 16 byte header. It contains instructions which my application needs. The processDeviceData
function returns the request for the next information.
Components of the application ISession
is the data collector root class. IsessionStatusCall
and IsessionVideoWakeup
are the sub components for the StatusCall
and Videowakeup
calls. IDeviceDataHandler
is a root for the data process class. IStatusChangeDeviceDataHandler
, IVideoWakeupDeviceDataHandler
are sub interface for video and status. If you look at the code you will find the use of these classes. Using these strategy patterns, we can achieve device traffic communication.
interface ISession
interface ISessionStatusCall : ISession
interface ISessionVideoWakeup:ISession
interface IStatusChangeDeviceDataHandler : IDeviceDataHandler
interface IVideoWakeupDeviceDataHandler : IDeviceDataHandler
The Socket.IOControl
Property plays very vital role in performance.
ICMP unreachable error occurs when a router receives a datagram that requires fragmentation, but the “don't fragment” (DF) flag is turned on in the IP header. This error can be used by a program that needs to determine the smallest MTU in the path to a destination-called the path MTU discovery mechanism
Below is the code to avoid this problem.
public const int SIO_UDP_CONNRESET = -1744830452;
Then set the low level io control to ignore these messages:
var client = new UdpClient(endpoint);
client.Client.IOControl(IOControlCode)SIO_UDP_CONNRESET,
new byte[] {0, 0, 0, 0 },null);
Points of Interest
While writing this code I learnt that you can write a UDP server which is capable of handling multiple requests on one port. The benefit of using UDP is that it is state less so there is no problem of managing ack info. The data trace log functionality is also used for writing the received data after processing. The fun thing about UDP is some times you need to ask data twice from the Device and UDP will give you two data packets: one which we asked previously and one which you asked after that.