Shadowrun: Awakened 29 September 2011 - Build 871
SphynxServer.hpp
Go to the documentation of this file.
00001 /*
00002     Copyright (c) 2009-2010 Christopher A. Taylor.  All rights reserved.
00003 
00004     Redistribution and use in source and binary forms, with or without
00005     modification, are permitted provided that the following conditions are met:
00006 
00007     * Redistributions of source code must retain the above copyright notice,
00008       this list of conditions and the following disclaimer.
00009     * Redistributions in binary form must reproduce the above copyright notice,
00010       this list of conditions and the following disclaimer in the documentation
00011       and/or other materials provided with the distribution.
00012     * Neither the name of LibCat nor the names of its contributors may be used
00013       to endorse or promote products derived from this software without
00014       specific prior written permission.
00015 
00016     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00017     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00018     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00019     ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00020     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00021     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00022     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00023     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00024     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00025     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00026     POSSIBILITY OF SUCH DAMAGE.
00027 */
00028 
00029 #ifndef CAT_SPHYNX_SERVER_HPP
00030 #define CAT_SPHYNX_SERVER_HPP
00031 
00032 #include <cat/net/SphynxTransport.hpp>
00033 #include <cat/threads/RWLock.hpp>
00034 #include <cat/threads/Thread.hpp>
00035 #include <cat/threads/WaitableFlag.hpp>
00036 #include <cat/net/SphynxCollexion.hpp>
00037 #include <cat/crypt/cookie/CookieJar.hpp>
00038 #include <cat/crypt/tunnel/KeyAgreementResponder.hpp>
00039 
00040 namespace cat {
00041 
00042 
00043 namespace sphynx {
00044 
00045 
00046 /*
00047     Designed for server hardware with many processors.
00048 
00049     In order to handle many users, the Sphynx server opens up a single UDP port
00050     to accept new connections, and several other UDP data ports for data.
00051 
00052     For retransmissions and detecting link loss due to timeout, the server runs
00053     several additional threads that wake up periodically to perform timed tasks.
00054 
00055     Server uses thread pool to receive packets on connect and worker ports,
00056     meaning that packets are processed by any free CPU as soon as they arrive.
00057 
00058     Sphynx Server
00059         UDP Hello Port [1]
00060             + In case this thread spins constantly, only use one CPU for new
00061               connections since in-game experience is more important than login
00062             + Assigns users to a data port after handshake completes
00063 
00064         UDP Data Ports [4 * (CPU Count)]
00065             + Spread users evenly across several ports since
00066               only one packet can be processed from a single port at a time
00067             + Any free CPU will process incoming packets as fast as possible
00068 
00069         ServerTimer threads [(CPU Count) / 2]
00070             + In case these threads spin constantly, they only consume
00071               half of the CPU resources available
00072             + Wake up every X milliseconds according to Transport::TICK_RATE
00073             + Detect link loss due to silence timeout
00074             + Update transport layer
00075                 + Retransmit lost messages
00076                 + Re-evaluate bandwidth and transmit queued messages
00077 */
00078 
00079 
00081 
00082 // Derive from sphynx::Connexion and sphynx::Server to define server behavior
00083 class Connexion : public Transport, public ThreadRefObject
00084 {
00085     friend class Server;
00086     friend class Map;
00087     friend class ServerWorker;
00088     friend class ServerTimer;
00089 
00090 public:
00091     Connexion();
00092     virtual ~Connexion() {}
00093 
00094 private:
00095     volatile u32 _destroyed;
00096 
00097     u32 _key; // Map hash table index, unique for each active connection
00098     Connexion *_next_delete;
00099     ServerWorker *_server_worker;
00100 
00101     u8 _first_challenge[64]; // First challenge seen from this client address
00102     u8 _cached_answer[128]; // Cached answer to this first challenge, to avoid eating server CPU time
00103 
00104 private:
00105     // Return false to destroy this object
00106     bool Tick(ThreadPoolLocalStorage *tls, u32 now);
00107 
00108     void OnRawData(ThreadPoolLocalStorage *tls, u8 *data, u32 bytes);
00109 
00110     virtual bool PostPacket(u8 *buffer, u32 buf_bytes, u32 msg_bytes, u32 skip_bytes);
00111 
00112 public:
00113     CAT_INLINE bool IsValid() { return _destroyed == 0; }
00114     CAT_INLINE u32 GetKey() { return _key; }
00115 
00116     void Destroy();
00117 
00118 protected:
00119     NetAddr _client_addr;
00120 
00121     // Last time a packet was received from this user -- for disconnect timeouts
00122     u32 _last_recv_tsc;
00123 
00124     bool _seen_encrypted;
00125     AuthenticatedEncryption _auth_enc;
00126 
00127 protected:
00128     virtual void OnConnect(ThreadPoolLocalStorage *tls) = 0;
00129     virtual void OnDestroy() = 0;
00130     virtual void OnTick(ThreadPoolLocalStorage *tls, u32 now) = 0;
00131 };
00132 
00133 
00135 
00136 class Map
00137 {
00138 protected:
00139     CAT_INLINE u32 hash_addr(const NetAddr &addr, u32 salt);
00140 
00141 public:
00142     struct Slot
00143     {
00144         Connexion *connection;
00145         bool collision;
00146         Slot *next;
00147     };
00148 
00149 protected:
00150     u32 _hash_salt;
00151     CAT_ALIGNED(16) Slot _table[HASH_TABLE_SIZE];
00152     RWLock _table_lock;
00153 
00154 public:
00155     Map();
00156     virtual ~Map();
00157 
00158     // Lookup client by address
00159     Connexion *Lookup(const NetAddr &addr);
00160 
00161     // Lookup client by key
00162     Connexion *Lookup(u32 key);
00163 
00164     // May return false if network address in Connexion is already in the map.
00165     // This averts a potential race condition but should never happen during
00166     // normal operation, so the Connexion allocation by caller won't be wasted.
00167     bool Insert(Connexion *conn);
00168 
00169     // Destroy a list described by the 'next' member of Slot
00170     void DestroyList(Map::Slot *kill_list);
00171 
00172     void Tick(ThreadPoolLocalStorage *tls);
00173 };
00174 
00175 
00177 
00178 class ServerWorker : public UDPEndpoint
00179 {
00180     friend class Map;
00181 
00182 protected:
00183     Map *_conn_map;
00184     ServerTimer *_server_timer;
00185 
00186 protected:
00187     volatile u32 _session_count;
00188 
00189 public:
00190     ServerWorker(Map *conn_map, ServerTimer *server_timer);
00191     virtual ~ServerWorker();
00192 
00193     void IncrementPopulation();
00194     void DecrementPopulation();
00195     u32 GetPopulation() { return _session_count; }
00196 
00197 protected:
00198     void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
00199     void OnWrite(u32 bytes) {}
00200     void OnClose();
00201 };
00202 
00203 
00205 
00206 class ServerTimer : Thread
00207 {
00208 protected:
00209     Map *_conn_map;
00210 
00211 protected:
00212     ServerWorker **_workers;
00213     int _worker_count;
00214 
00215 protected:
00216     Map::Slot *_insert_head;
00217     Mutex _insert_lock;
00218 
00219 protected:
00220     Map::Slot *_active_head;
00221 
00222 public:
00223     ServerTimer(Map *conn_map, ServerWorker **workers, int worker_count);
00224     virtual ~ServerTimer();
00225 
00226     CAT_INLINE bool Valid() { return _worker_count > 0; }
00227 
00228 public:
00229     void InsertSlot(Map::Slot *slot);
00230 
00231 protected:
00232     CAT_INLINE void Tick(ThreadPoolLocalStorage *tls);
00233     bool ThreadFunction(void *param);
00234 
00235 protected:
00236     static const int TIMER_THREAD_KILL_TIMEOUT = 10000;
00237 
00238     WaitableFlag _kill_flag;
00239 };
00240 
00241 
00243 
00244 class Server : public UDPEndpoint
00245 {
00246 public:
00247     Server();
00248     virtual ~Server();
00249 
00250     bool StartServer(ThreadPoolLocalStorage *tls, Port port, u8 *public_key, int public_bytes, u8 *private_key, int private_bytes, const char *session_key);
00251 
00252     u32 GetTotalPopulation();
00253 
00254     static bool GenerateKeyPair(ThreadPoolLocalStorage *tls, const char *public_key_file,
00255         const char *private_key_file, u8 *public_key,
00256         int public_bytes, u8 *private_key, int private_bytes);
00257 
00258 private:
00259     static const int SESSION_KEY_BYTES = 32;
00260     char _session_key[SESSION_KEY_BYTES];
00261 
00262     Port _server_port;
00263     Map _conn_map;
00264 
00265     CookieJar _cookie_jar;
00266     KeyAgreementResponder _key_agreement_responder;
00267     u8 _public_key[PUBLIC_KEY_BYTES];
00268 
00269     static const int WORKER_LIMIT = 32; // Maximum number of workers
00270     ServerWorker **_workers;
00271     int _worker_count;
00272 
00273     ServerTimer **_timers;
00274     int _timer_count;
00275 
00276 private:
00277     ServerWorker *FindLeastPopulatedPort();
00278 
00279     void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
00280     void OnWrite(u32 bytes);
00281     void OnClose();
00282 
00283 protected:
00284     // Must return a new instance of your Connexion derivation
00285     virtual Connexion *NewConnexion() = 0;
00286 
00287     // IP address filter: Return true to allow the connection to be made
00288     virtual bool AcceptNewConnexion(const NetAddr &src) = 0;
00289 
00290     // Lookup client by key
00291     Connexion *Lookup(u32 key);
00292 };
00293 
00294 
00295 } // namespace sphynx
00296 
00297 
00298 } // namespace cat
00299 
00300 #endif // CAT_SPHYNX_SERVER_HPP

Copyright © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.

GNU Lesser General Public License 3 Sourceforge.net