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

Example Servers - Echo Server Service

This example shows you how to build another simple server. The basic structure of the TCP server is similar to the Basic 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 Echo Server example and this example.

This example requires the "Windows Services" licensing option of The Server Framework and it requires libraries that only ship with that option (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.

This server runs as a Windows Service using the JetByte Service Tools Library. This means that the way we put together the components that make up the server is quite different to the non service based example servers. The reason for this is that we no longer have a main thread that we can use to create the objects on the stack with and which will then wait around until the server shuts down. We now have to create our objects on the heap during the start up phase of our service and destroy them during the shutdown phase.

Our example server consists of five source files. The ServerMain.cpp is considerably different to the non service samples. The main for our service is boiler-plate code which is implemented by the library and we use a macro to instantiate this code and configure it.

 #include "JetByteTools\Admin\Admin.h"

 #include "Service.h"

 #pragma hdrstop

 #include "JetByteTools\ServiceTools\ServiceMain.h"

 ///////////////////////////////////////////////////////////////////////////////
 // Implement our main()
 ///////////////////////////////////////////////////////////////////////////////

 IMPLEMENT_SERVICE_MAIN(_T("Echo Server Service"), CService, _T("EchoServerService.log"));

There are actually two "Service Main" macros in the Service Tools Library. The first, used above, allows you to provide a name for the service, which is used in some error message reports, a service class which is where your code lives and a log file name.
The second macro, IMPLEMENT_SERVICE_MAIN_WITH_CUSTOM_LOG, allows you to provide a custom /ref DebugTraceInfo "debug trace log" class.
The SocketServer.cpp and SocketServer.h files are where your server is implemented and these are, by and large, unchanged from the ones that are presented in the Basic Echo Server example.
The main work is done in the CService class and the Service.cpp and Service.h files. Your CService class derives directly from JetByteTools::Service::CService, though I expect that to change in the next release. You provide your functionality by selectively overriding virtual methods on your base class, the "Template Method Pattern", rather than on an interface specifically for that purpose, the "Strategy Pattern" which is common throughout most of the other libraries.

 class CService : public JetByteTools::Service::CService
 {
    public :

       CService();

       virtual ~CService();

    private :

       // Implement CService interface

       virtual void OnServiceInstalled();
       virtual void OnServiceRemoved();

       virtual void InitialiseService(
          JetByteTools::Service::INotifyProgress &notify);

       virtual void ContinueService(
          JetByteTools::Service::INotifyProgress &notify);

       virtual void PauseService(
          JetByteTools::Service::INotifyProgress &notify);

       virtual void StopService(
          JetByteTools::Service::INotifyProgress &notify);

       virtual void UninitialiseService(
          JetByteTools::Service::INotifyProgress &notify);

       virtual void OnException(
          const JetByteTools::Win32::CException &e);

       virtual void OnException(
          const JetByteTools::Win32::CSEHException &e);

       virtual void OnException();

The most important places in this example server are InitialiseService() and UninitialiseService() as these are where the server object is constructed and destroyed.

 void CService::InitialiseService(
    INotifyProgress &notify)
 {
    notify.Notify(1000);

    m_pLockFactory = new CSharedCriticalSection(47);

    m_pPool = new CIOPool(0);      // number of threads (0 = 2 x processors)

    m_pPool->Start();

    notify.Notify(1000);

    m_pSocketAllocator = new CSequencedStreamSocketAllocator(
       *m_pLockFactory,
       10);                          // max number of sockets to keep in the pool

    m_pBufferAllocator = new CBufferAllocator(
       1024,                         // buffer size
       10);                          // max number of buffers to keep in the pool

    notify.Notify(1000);

    const CAddressIPv4 address(
       INADDR_ANY,                   // address to listen on
       5050);                        // port to listen on

    const size_t listenBacklog = 5;

    m_pLimiter = new CConnectionLimiter(CConnectionLimiter::NoLimit);

    m_pServer = new CSocketServer(
       "Welcome to echo server\r\n",
       address,
       listenBacklog,
       *m_pPool,
       *m_pSocketAllocator,
       *m_pBufferAllocator,
       *m_pLimiter);

    notify.Notify(5000);

    m_pServer->Start();

    notify.Notify(1000);
 }

The first thing that you'll notice is that unlike the command line server examples there's currently no way to configure this server without recompiling. It's left as an exercise for the reader to configure the service from the Windows Registry or via a configuration file, etc. The other major change is that we dynamically allocate all of the objects that we need. These are then cleaned up in UninitialiseService(). Apart from that the sequence of events is pretty similar to what happens in non service based example servers. We create the objects that we need to be able to create our server object and then we create our server object and start it up.

Note that we continually notify the Service Control Manager with hints about how long the next phase of service start up will take us.

As you can see from the following code, your shutdown is as expected.

 void CService::UninitialiseService(
    INotifyProgress &notify)
 {
    notify.Notify(5000);

    m_pServer->WaitForShutdownToComplete();

    notify.Notify(5000);

    m_pPool->WaitForShutdownToComplete();

    notify.Notify(5000);

    m_pBufferAllocator->Flush();

    notify.Notify(5000);

    m_pSocketAllocator->ReleaseSockets();

    delete m_pServer;

    m_pServer = 0;

    delete m_pLimiter;

    m_pLimiter = 0;

    delete m_pPool;

    m_pPool = 0;

    delete m_pBufferAllocator;

    m_pBufferAllocator = 0;

    delete m_pSocketAllocator;

    m_pSocketAllocator = 0;

    delete m_pLockFactory;

    m_pLockFactory = 0;
 }

We simply shut down the various objects and delete them.

Our base class, JetByteTools::Service::CService, deals with service installation, command line processing, service removal and all interactions between our service and the Service Control Manager.

Some of the main advantages in using the JetByte Service Tools Library are that:
  • It provides you with an easy way to host your code as a Windows Service
  • It provides a seamless method for debugging and developing your code within the Visual Studio IDE.
  • It provides you with the option of supporting running multiple instances of the same service executable in the SCM.


The services that are constructed with the library support a command line switch /debug which runs the service in "debug mode". This removes all interaction with the Service Control Manager and simply runs your process as a normal executable. Because of this you can run your service in the IDE and debug it as a normal executable and the library deals with mocking out all of the Service Control Manager interaction. When running in "debug mode" you can also use the server shutdown utility to send "stop", "pause" and "resume" events to your service in the same way that the Service Control Manager would do if you were running as a real service. We've found that being able to develop services in this way has a positive effect on productivity.

When you construct your service object you have the option of passing true to a constructor parameter which specified if "multiple instances" are supported. Multiple instances are a powerful feature of the JetByte Service Tools Library that allow you to install a service multiple times, each with a unique instance name. You can then run each service independently and when they start they have access to the instance name that they're running under. This allows you to install and run a service configured in multiple ways or providing access to different servers on different ports, or whatever. See the Echo Server Multi Instance Service example for more details.

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