ClanLib Tutorial – Part 4: Server as a Service

In the previous part, we ran our server as a console application. While this is fine for debugging purposes, it isn’t very server-friendly. In this part we’ll easily convert the server to be a proper OS service, or daemon as it is called on linux. We’ll also convert it to be a threaded application.

Creating an OS Service

In ClanLib there is a class called CL_Service, which handles all the OS integration for running your application as a service. It adds functionality for installing, starting and stopping your service.

#pragma once

#include "server.h"

class ServerService : public CL_Service
{
public:
	ServerService();
	virtual ~ServerService() {}

	virtual void service_start(std::vector<CL_String> &args);
	virtual void service_stop();
	virtual void service_reload() {}

private:
	Server server;
};

Here we have created a new class that inherits CL_Service. As you can see, you get three virtual functions from the base class – start, stop and reload. We will only cover start and stop here, but for your interest, reload is called when the service is asked to reload its configuration.

#include "precomp.h"
#include "server_service.h"

ServerService::ServerService()
: CL_Service("ClanLibGameServer")
{
}

void ServerService::service_start(std::vector<CL_String> &args)
{
	server.start();
}

void ServerService::service_stop()
{
	server.stop();
}

In the implementation, we basically give the service a name, and we launch or stop the main server class.

Back to our application entry point in main.cpp, we need to do some small changes. First of all, you might notice we are including a file called “precomp.h”. This is a file including all the external stable include files, like the ClanLib includes. We use this to create a precompiled header file, and it makes the compilation go alot faster on subsequent compiles. Secondly, we also instantiate a FileLogger in addition to the ConsoleLogger we had earlier. This will log all out cl_log_event calls to both the console window and a file called “server.log”.

But the biggest change is of course to instantiate the ServerService class instead of the Server as we did before. The CL_Service class has a main() function that we need to call, and it expects the command line arguments from the launched application.

#include "precomp.h"
#include "server_service.h"

int main(int argc, char**argv)
{
	try
	{
		CL_SetupCore setup_core;
		CL_SetupNetwork setup_network;

		CL_ConsoleLogger logger;
		CL_FileLogger file_logger("server.log");

		ServerService server_service;
		return server_service.main(argc, argv);
	}
	catch (CL_Exception &e)
	{
		CL_Console::write_line("Unhandled Exception: %1", e.get_message_and_stack_trace());
		return 1;
	}
}

Launching and installing the service

If we launch the application now, we’ll get an output something like this:

Note that under Linux the output will be slightly different. There is no install/uninstall arguments, but rather a “-daemon” argument.

Our service application now expects some command line parameters. As we can see from the output, we now have the possibility of installing and uninstalling the service, but also debug it. Debug basically means we can run it without having to install it first.

If we run “ServerApplication -install” as a Administrative user, the service will install itself.

Note that under Linux, you will have to run “ServerApplication -daemon <pidfile>”. This does not “install” under Linux, but at least lets you run it as service. Look up your favorite Linux distribution documentation how to install it more permanently.

Making life easier during development

When doing development, it becomes a hassle to have to install the server all the time. Basically we got two choices to make this easier. Either we configure our IDE to start the application with the “-debug” command line argument, or we hack the code! I opted for the hack, since I tend to send builds around to friends, and it becomes easier for them to run the server.

        ServerService server_service;
        if(argc == 1) // Default to debug mode for Service
        {
            char *debug_argv[2] = { argv[0], "-debug" };
            return server_service.main(2, debug_argv);
        }
        else
        {
            return server_service.main(argc, argv);
        }

Ok, might not be the prettiest code, but it works by overriding the command line arguments if none were supplied. So if you run ServerApplication without any arguments, we force it into debug mode. It is nice for a development phase, but I would not leave it in for production.

Converting server to be threaded

We’re also going to change the main server class to start itself in a separate thread. This way, when Windows asks the service to start, we basically fire up a thread and return immediately.

CL_Thread is a very easy to use thread class in ClanLib. Call its start() function, and provide the function it should start in a separate thread. There are also possibility to pass some arguments to the function if needed.

We also add a CL_Event called stop_event. Remember in the last part we mentioned that CL_Event is a handy mechanism for programs to sleep waiting for something external to happen. We used it to listen for any network events. Now we add a new event, namely a stop event. This event will be fired if the user decides to shut down the service for instance.

In Server, we add start() and stop() functions, which will handle starting up the thread and firing off the stop_event when we’re closing down. In the exec() function, we now listen for two events – network_server.get_event_arrived() and our own stop_event. If any of those two occur, we check the wakeup reason, which is the index of which event was fired. If it was the network event, we process those. Otherwise, if it was the stop event, we exit our main loop.

Server::~Server()
{
	stop();
}

void Server::start()
{
	stop();
	stop_event.reset();

	thread.start(this, &Server::exec);
}

void Server::stop()
{
	stop_event.set();
	thread.join();
}

void Server::exec()
{
	network_server.start("4556");

	cl_log_event("system", "SERVER started");

	while (true)
	{
		int wakeup_reason = CL_Event::wait(network_server.get_event_arrived(), stop_event);
		if (wakeup_reason == 0)	// network activity was flagged
			network_server.process_events();
		else if (wakeup_reason == 1) // stop event was flagged
			break;
	}

	network_server.stop();
}

Download source code and project files for this part

We now have a somewhat more robust server infrastructure. I’m sorry if it was kinda dry this time, but the next time we do server application coding, we’ll go into more game play related code. But in the next few parts, we’ll do a lot more on the client side; more visuals, GUI and gameplay! Stay tuned!

About these ads
  1. #1 by Sebas on April 24, 2011 - 07:09

    Thanks for the tutorials. I’m just starting to learn a few things about ClanLib and these tutorials have been really helpful. I hope you’ll write another.

  2. #2 by Tuisto on May 21, 2011 - 21:13

    Hey,
    i really like ClanLib und your tutorials. However, why aren’t there any new ones? This one is from 1st November 2010.
    Please keep up the good work!

    I want to create a small pong game which is online playable.

    I tried to create a small client/server app and just showed some circles with the player names above them using hamachi.
    For some reason it is laggy as hell and I think that I am doing something wrong and want to know more about the networking part of ClanLib!

    I really hope that you are going to continue these tutorials
    Thank you!

  3. #3 by Jackson on June 12, 2011 - 20:37

    As has been said, really promising start on these tutorials, and it’s great to see someone making an effort on behalf of the ClanLib.
    Would love to see the next part of the tutorial. Please consider CL_KeepAlive::process()

    … Yeah, terrible joke I know :)

  4. #4 by Dave M on June 27, 2011 - 06:20

    Hahahah I totally figured a pong game would be the perfect way to practice and test this lib and the networking stuff specifically.

    Looking forward to seeing more information on this library, just stumbled upon it and I already like it before writing a single line of code. :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: