The C++ framework for developing highly scalable, high performance servers on Windows platforms.

Example Servers - UDP Echo Server

This example shows you how to build the simplest UDP server that you can build. The basic structure of this server is also described in the UDP Socket Server How To document. The server example uses a helper library, ServerCommon, which provides some utility code that many of the example server use. Things such as command line parsing, allocators and server callback implementations that display debug traces to show what they're doing. The basic structure of this server is similar to the Basic TCP Echo Server example and you should go and read about that first and have a good understanding of how everything fits together. This document will only cover the differences between the Basic TCP Echo Server example and this example.

This example is shipped with all licensed versions of The Server Framework and it requires the core server framework libraries (see here for licensing options). You can always download the latest version of this example from here; and although you will need the correct libraries to be able to build it you can look at the example code and see how it works and perhaps get ideas from it. A compiled, unicode release, build of this example is available on request if you require it for performance analysis of the framework.

The ServerMain.cpp file for this example is very similar to the TCP server example. The only differences are that the socket allocator is an instance of JetByteTools::Socket::CDatagramServerSocketAllocator and the server doesn't take a welcome message string.

The server itself is also similar to the TCP example. We implement fewer callbacks as all we need to handle is OnReadCompleted() as there is no 'connection establishment' phase with UDP as it is connectionless.

The constructor connects together the various support objects in the same was as a TCP server does. There's no 'connection' with UDP servers so there's no need to handle connection establishment. Once the server is listening, nothing happens until the datagram arrives and a read completes.

 void CSocketServer::OnReadCompleted(
    IDatagramServerSocket &socket,
    IBuffer &buffer)
 {
    try
    {
       Output(_T("ReadCompleted - socket: ") + ToString(&socket) + _T(" - ") + CAddressRenderer::AsString(socket.GetRemoteAddress(), true));

       EchoMessage(socket, buffer);
    }
    catch(const CException &e)
    {
       Output(_T("ReadCompleted - Exception - ") + e.GetDetails());
    }
    catch(...)
    {
       Output(_T("ReadCompleted - Unexpected exception"));
    }
 }
When the read completes our OnReadCompleted() handler is called and we are given a buffer which contains the bytes that made up the datagram.

It's good practice to protect the framework from exceptions that you throw whilst working in a callback method, you don't have to, the framework will catch anything that comes blasting out of your handlers but it's generally better to do your own cleanup work. We simply issue a debug message.

Since we're just a simple server our business logic doesn't care about what data has been sent to us, we just pass the bytes that we've been given to the EchoMessage() function and, well, this is what it does with them:
 void CSocketServer::EchoMessage(
    IDatagramServerSocket &socket,
    IBuffer &buffer) const
 {
    DEBUG_ONLY(Output(_T(" Local Address: ") + CAddressRenderer::AsString(socket.GetLocalAddress(), true)));
    DEBUG_ONLY(Output(_T("Remote Address: ") + CAddressRenderer::AsString(socket.GetRemoteAddress(), true)));

    DEBUG_ONLY(Output(_T("Data Echoed -\r\n") + DumpData(buffer.GetMemory(), buffer.GetUsed(), 60, true)));

    socket.Write(buffer);
 }
All we do is display what we're echoing (in debug builds only), and where it has come from and then write it back to the client.

Note that we don't issue a new read request in our read handler. UDP servers treat all datagrams as separate, even those from the same client address and port. There is no "linkage" between one datagram and another and so the server itself maintains pending reads so that it can pass the data to your callbacks when datagrams arrive. If you need to deal with datagrams from the same client address and port as a 'stream' of some sort then you will need to write that into your business logic. Some of the later server examples demonstrate how you might go about doing this.

Configuring and setting up a UDP server is very similar to setting up a TCP server. The ServerMain.cpp is pretty much identical except for the type of socket allocator used.

Construction is similar to the basic TCP server and the read completion is simpler becase we don't need to issue another read when this completion completes. Since our server listens on a single port for UDP datagrams and UDP is connectionless, all datagrams are considered to be equal and there are multiple pending reads within the server to deal with them.

 void CSocketServer::OnReadCompleted(
    IDatagramServerSocket &socket,
    IBuffer &buffer)
 {
    try
    {
       Output(_T("ReadCompleted - socket: ") + ToString(&socket) + _T(" - ") + CAddressRenderer::AsString(socket.GetRemoteAddress(), true));

       EchoMessage(socket, buffer);
    }
    catch(const CException &e)
    {
       Output(_T("ReadCompleted - Exception - ") + e.GetDetails());
    }
    catch(...)
    {
       Output(_T("ReadCompleted - Unexpected exception"));
    }
 }



Generated on Sun Sep 12 19:06:45 2021 for The Server Framework - v7.4 by doxygen 1.5.3