Simple echo server
#include "aconnect/aconnect.hpp" #include "aconnect/util.hpp"usingnamespace 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 client.writeResponse(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 server.join(); }
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 settingsstruct ServerSettings { int backlog; int domain; bool reuseAddr; bool enablePooling; int workersCount; int socketReadTimeout; // secint 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 - intclass 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).