Simple echo server

#include "aconnect/aconnect.hpp"
#include "aconnect/util.hpp"

using namespace aconnect;

// global server instance
Server  server;

void threadProc (const ClientInfo& client) { 
    static EndMarkSocketStateCheck check; 
    string request = client.getRequest (check); 
    request.erase ( request.find(check.endMark()) );

    string response;
    bool stopServer = false;
    if (util::equals (request, "STOP") ) { 
        response = "Processed"; 
        stopServer = true;
    } else { 
        response = "Echo: " + request; 
    // write response 
    if (stopServer)
        exit (0); 

// test it: http://localhost:8888/
int main (int argc, char* args[]) 
    Initializer init;
    FileLogger logger;
    // {timestamp} - will be replaced with generated timestamp (example: 22_05_2008_20_17_35), 
    // third parameter - max. size of log file - it will be rotated automatically
    logger.init (Log::Debug, "c:\\temp\\server_log_{timestamp}.log", 4194304);

    // init command server
    ServerSettings settings;
    settings.socketReadTimeout = settings.socketWriteTimeout = 300; // sec

    // init server
    server.setLog ( &logger); 
    server.init (8888, threadProc, settings);

    server.start(); // started in child thread

Initializer is an RAII-style guard to initialize OS-depended network functionality - under Windows it calls WSAStartup in constructor and WSACleanup in destructor.
Server is a main functional class - it creates TCP server socket, binds it to selected port (8888 in code) and start listening on this port. Server can be started in background thread (as in example) or in main execution thread: server.start (true).
At server initialization ServerSettings object is applied to server.

// server settings storage - used to setup default server settings
struct ServerSettings 
	int	backlog;
	int	domain;
	bool	reuseAddr;
	bool	enablePooling;
	int	workersCount;

	int	socketReadTimeout;		// sec
	int	socketWriteTimeout;		// sec

	// default settings
	ServerSettings () : 
		backlog (SOMAXCONN),	// backlog in listen() call 
		domain (AF_INET),	// domain for 'socket' function call
		reuseAddr (false),	// SO_REUSEADDR flag setup on server socket
		enablePooling (true),	// show whether create worker-threads pool or not
		workersCount (500),	// maximum worker-threads count
		socketReadTimeout (60),	// server socket SO_RCVTIMEO timeout
		socketWriteTimeout (60) // server socket SO_SNDTIMEO timeout
	{ }

Each accepted TCP connection is processed in background worker thread - portable Boost.Thread library us used. Simple threads pool implemented for aconnect::Server using boost::mutex and boost::condition_variable. If enablePooling field in server settings is true then when initial TCP interaction is finished worker thread starts waiting for new request time (returned to pool). ThreadPool class can be used separately, see ThreadPool examples.

When server accepted client TCP connection then it fills ClientInfo object with client related data.
struct ClientInfo
	port_type	port;		// int
	ip_addr_type	ip;		// unsigned char[4]
	socket_type	socket;		// OS-depended, under Win32 - SOCKET, Linux - int
	class Server	*server;

After client information loading execution is transferred to worker thread (new or borrowed from pool) that executes thread procedure (threadProc in code).

FileLogger - aconnect::Logger interface implementation to log messages to file. aconnect::Logger is a simple example of logging functionality developed in log4... manner - it contains set of logging methods: info, warn, error to log message with appropriate level.

ConsoleLogger writes messages to std::cout and FileLogger writes messages to file, FileLogger can rotate files when maximum file size achieved, BackgroundFileLogger is an extended file logger that collects messages to internal queue and flush them in background thread. FileLogger initialization is too simple - just define log level, path to file and maximum size of one log file (default size: 4 Mb).

Last edited Dec 7, 2009 at 2:28 PM by artiz, version 6


No comments yet.