/* * Copyright (C) 2007-2023 Xagasoft, All rights reserved. * * This file is part of the libbu++ library and is released under the * terms of the license contained in the file LICENSE. */ #ifndef BU_SERVER_H #define BU_SERVER_H #include #include "bu/string.h" #include "bu/list.h" #include "bu/clientlink.h" #include "bu/clientlinkfactory.h" #include "bu/hash.h" #include "bu/synchroqueue.h" #include "bu/thread.h" #include "bu/counterevent.h" #include "bu/config.h" #ifndef PROFILE_BU_SERVER // #define PROFILE_BU_SERVER 1 #endif #ifdef PROFILE_BU_SERVER #include "bu/profiler.h" #endif namespace Bu { class ServerSocket; class Socket; class Client; /** * Core of a network server. This class is distinct from a ServerSocket in * that a ServerSocket is one listening socket, nothing more. Socket will * manage a pool of both ServerSockets and connected Sockets along with * their protocols and buffers. * * To start serving on a new port, use the addPort functions. Each call to * addPort creates a new ServerSocket, starts it listening, and adds it to * the server pool. * * All of the real work is done by scan, which will wait for up * to the timeout set by setTimeout before returning if there is no data * pending. scan should probably be called in some sort of tight * loop, possibly in it's own thread, or in the main control loop. * * In order to use a Server you must subclass it and implement the pure * virtual functions. These allow you to receive notification of events * happening within the server itself, and actually makes it useful. *@ingroup Serving */ class Server { friend class ServerInterface; public: Server( int iIoWorkers=4, int iClientWorkers=8 ); virtual ~Server(); #ifdef WIN32 typedef unsigned int fd; #else typedef int fd; #endif void addServerSocket( Bu::ServerSocket *pSocket ); virtual void scan(); void setTimeout( int nTimeoutSec, int nTimeoutUSec=0 ); void addClient( const Bu::ServerSocket *pSrv, Bu::Socket *pSocket ); Bu::Client *getClient( fd iId ); bool getClientAndSocket( fd iId, Bu::Client *&pClient, Bu::Socket *&pSocket ); void setAutoTick( bool bEnable=true ); void tick(); virtual void onNewConnection( const Bu::ServerSocket *pSrv, Client *pClient, Bu::Socket *pSocket )=0; virtual void onClosedConnection( Client *pClient )=0; void shutdown(); protected: void clientReadReady( fd iFd ); void clientWriteReady( fd iFd ); void closeClient( fd iSocket ); private: class SrvClientLink : public Bu::ClientLink { public: SrvClientLink( Bu::Client *pClient ); virtual ~SrvClientLink(); virtual void sendMessage( const Bu::String &sMsg ); private: Bu::Client *pClient; }; class SrvClientLinkFactory : public Bu::ClientLinkFactory { public: SrvClientLinkFactory(); virtual ~SrvClientLinkFactory(); virtual Bu::ClientLink *createLink( Bu::Client *pClient ); }; class WriteMonitor : public Bu::Thread { public: WriteMonitor( Server &rSrv ); virtual ~WriteMonitor(); protected: virtual void run(); private: Server &rSrv; }; class Event { public: enum Operation { Read, Write, Process }; Event( fd iId, Operation eOp ); ~Event(); fd getId() const; Operation getOperation() const; private: fd iId; Operation eOp; }; typedef Bu::SynchroQueue EventQueue; class IoWorker : public Bu::Thread { public: IoWorker( Server &rSrv ); virtual ~IoWorker(); protected: virtual void run(); private: void handleRead( Client *pClient, Socket *pSocket ); void handleWrite( Client *pClient, Socket *pSocket ); private: Server &rSrv; }; friend class Bu::Server::IoWorker; class ClientWorker : public Bu::Thread { public: ClientWorker( Server &rSrv ); virtual ~ClientWorker(); protected: virtual void run(); private: Server &rSrv; }; friend class Bu::Server::ClientWorker; class __ServerCore *pCore; int nTimeoutSec; int nTimeoutUSec; typedef Hash SrvHash; SrvHash hServers; typedef Hash ClientHash; typedef Hash SocketHash; ClientHash hClients; SocketHash hSockets; bool bAutoTick; Bu::Mutex mClients; Bu::Mutex mScan; Bu::Mutex mWorkers; EventQueue qIoEvent; EventQueue qClientEvent; typedef List IoWorkerList; typedef List ClientWorkerList; IoWorkerList lIoWorker; ClientWorkerList lClientWorker; WriteMonitor tMonitorWrite; bool bRunning; }; } #endif