| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- /*
- Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * Neither the name of LibCat nor the names of its contributors may be used
- to endorse or promote products derived from this software without
- specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- */
- #ifndef CAT_SPHYNX_SERVER_HPP
- #define CAT_SPHYNX_SERVER_HPP
- #include <cat/net/SphynxTransport.hpp>
- #include <cat/threads/RWLock.hpp>
- #include <cat/threads/Thread.hpp>
- #include <cat/threads/WaitableFlag.hpp>
- #include <cat/net/SphynxCollexion.hpp>
- #include <cat/crypt/cookie/CookieJar.hpp>
- #include <cat/crypt/tunnel/KeyAgreementResponder.hpp>
- namespace cat {
- namespace sphynx {
- /*
- Designed for server hardware with many processors.
- In order to handle many users, the Sphynx server opens up a single UDP port
- to accept new connections, and several other UDP data ports for data.
- For retransmissions and detecting link loss due to timeout, the server runs
- several additional threads that wake up periodically to perform timed tasks.
- Server uses thread pool to receive packets on connect and worker ports,
- meaning that packets are processed by any free CPU as soon as they arrive.
- Sphynx Server
- UDP Hello Port [1]
- + In case this thread spins constantly, only use one CPU for new
- connections since in-game experience is more important than login
- + Assigns users to a data port after handshake completes
- UDP Data Ports [4 * (CPU Count)]
- + Spread users evenly across several ports since
- only one packet can be processed from a single port at a time
- + Any free CPU will process incoming packets as fast as possible
- ServerTimer threads [(CPU Count) / 2]
- + In case these threads spin constantly, they only consume
- half of the CPU resources available
- + Wake up every X milliseconds according to Transport::TICK_RATE
- + Detect link loss due to silence timeout
- + Update transport layer
- + Retransmit lost messages
- + Re-evaluate bandwidth and transmit queued messages
- */
- //// sphynx::Connexion
- // Derive from sphynx::Connexion and sphynx::Server to define server behavior
- class Connexion : public Transport, public ThreadRefObject
- {
- friend class Server;
- friend class Map;
- friend class ServerWorker;
- friend class ServerTimer;
- public:
- Connexion();
- virtual ~Connexion() {}
- private:
- volatile u32 _destroyed;
- u32 _key; // Map hash table index, unique for each active connection
- Connexion *_next_delete;
- ServerWorker *_server_worker;
- u8 _first_challenge[64]; // First challenge seen from this client address
- u8 _cached_answer[128]; // Cached answer to this first challenge, to avoid eating server CPU time
- private:
- // Return false to destroy this object
- bool Tick(ThreadPoolLocalStorage *tls, u32 now);
- void OnRawData(ThreadPoolLocalStorage *tls, u8 *data, u32 bytes);
- virtual bool PostPacket(u8 *buffer, u32 buf_bytes, u32 msg_bytes, u32 skip_bytes);
- public:
- CAT_INLINE bool IsValid() { return _destroyed == 0; }
- CAT_INLINE u32 GetKey() { return _key; }
- void Destroy();
- protected:
- NetAddr _client_addr;
- // Last time a packet was received from this user -- for disconnect timeouts
- u32 _last_recv_tsc;
- bool _seen_encrypted;
- AuthenticatedEncryption _auth_enc;
- protected:
- virtual void OnConnect(ThreadPoolLocalStorage *tls) = 0;
- virtual void OnDestroy() = 0;
- virtual void OnTick(ThreadPoolLocalStorage *tls, u32 now) = 0;
- };
- //// sphynx::Map
- class Map
- {
- protected:
- CAT_INLINE u32 hash_addr(const NetAddr &addr, u32 salt);
- public:
- struct Slot
- {
- Connexion *connection;
- bool collision;
- Slot *next;
- };
- protected:
- u32 _hash_salt;
- CAT_ALIGNED(16) Slot _table[HASH_TABLE_SIZE];
- RWLock _table_lock;
- public:
- Map();
- virtual ~Map();
- // Lookup client by address
- Connexion *Lookup(const NetAddr &addr);
- // Lookup client by key
- Connexion *Lookup(u32 key);
- // May return false if network address in Connexion is already in the map.
- // This averts a potential race condition but should never happen during
- // normal operation, so the Connexion allocation by caller won't be wasted.
- bool Insert(Connexion *conn);
- // Destroy a list described by the 'next' member of Slot
- void DestroyList(Map::Slot *kill_list);
- void Tick(ThreadPoolLocalStorage *tls);
- };
- //// sphynx::ServerWorker
- class ServerWorker : public UDPEndpoint
- {
- friend class Map;
- protected:
- Map *_conn_map;
- ServerTimer *_server_timer;
- protected:
- volatile u32 _session_count;
- public:
- ServerWorker(Map *conn_map, ServerTimer *server_timer);
- virtual ~ServerWorker();
- void IncrementPopulation();
- void DecrementPopulation();
- u32 GetPopulation() { return _session_count; }
- protected:
- void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
- void OnWrite(u32 bytes) {}
- void OnClose();
- };
- //// sphynx::ServerTimer
- class ServerTimer : Thread
- {
- protected:
- Map *_conn_map;
- protected:
- ServerWorker **_workers;
- int _worker_count;
- protected:
- Map::Slot *_insert_head;
- Mutex _insert_lock;
- protected:
- Map::Slot *_active_head;
- public:
- ServerTimer(Map *conn_map, ServerWorker **workers, int worker_count);
- virtual ~ServerTimer();
- CAT_INLINE bool Valid() { return _worker_count > 0; }
- public:
- void InsertSlot(Map::Slot *slot);
- protected:
- CAT_INLINE void Tick(ThreadPoolLocalStorage *tls);
- bool ThreadFunction(void *param);
- protected:
- static const int TIMER_THREAD_KILL_TIMEOUT = 10000;
- WaitableFlag _kill_flag;
- };
- //// sphynx::Server
- class Server : public UDPEndpoint
- {
- public:
- Server();
- virtual ~Server();
- bool StartServer(ThreadPoolLocalStorage *tls, Port port, u8 *public_key, int public_bytes, u8 *private_key, int private_bytes, const char *session_key);
- u32 GetTotalPopulation();
- static bool GenerateKeyPair(ThreadPoolLocalStorage *tls, const char *public_key_file,
- const char *private_key_file, u8 *public_key,
- int public_bytes, u8 *private_key, int private_bytes);
- private:
- static const int SESSION_KEY_BYTES = 32;
- char _session_key[SESSION_KEY_BYTES];
- Port _server_port;
- Map _conn_map;
- CookieJar _cookie_jar;
- KeyAgreementResponder _key_agreement_responder;
- u8 _public_key[PUBLIC_KEY_BYTES];
- static const int WORKER_LIMIT = 32; // Maximum number of workers
- ServerWorker **_workers;
- int _worker_count;
- ServerTimer **_timers;
- int _timer_count;
- private:
- ServerWorker *FindLeastPopulatedPort();
- void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
- void OnWrite(u32 bytes);
- void OnClose();
- protected:
- // Must return a new instance of your Connexion derivation
- virtual Connexion *NewConnexion() = 0;
- // IP address filter: Return true to allow the connection to be made
- virtual bool AcceptNewConnexion(const NetAddr &src) = 0;
- // Lookup client by key
- Connexion *Lookup(u32 key);
- };
- } // namespace sphynx
- } // namespace cat
- #endif // CAT_SPHYNX_SERVER_HPP
|