SphynxServer.hpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*
  2. Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
  3. Redistribution and use in source and binary forms, with or without
  4. modification, are permitted provided that the following conditions are met:
  5. * Redistributions of source code must retain the above copyright notice,
  6. this list of conditions and the following disclaimer.
  7. * Redistributions in binary form must reproduce the above copyright notice,
  8. this list of conditions and the following disclaimer in the documentation
  9. and/or other materials provided with the distribution.
  10. * Neither the name of LibCat nor the names of its contributors may be used
  11. to endorse or promote products derived from this software without
  12. specific prior written permission.
  13. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  14. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  16. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  17. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  19. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  20. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  21. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  22. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  23. POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #ifndef CAT_SPHYNX_SERVER_HPP
  26. #define CAT_SPHYNX_SERVER_HPP
  27. #include <cat/net/SphynxTransport.hpp>
  28. #include <cat/threads/RWLock.hpp>
  29. #include <cat/threads/Thread.hpp>
  30. #include <cat/threads/WaitableFlag.hpp>
  31. #include <cat/net/SphynxCollexion.hpp>
  32. #include <cat/crypt/cookie/CookieJar.hpp>
  33. #include <cat/crypt/tunnel/KeyAgreementResponder.hpp>
  34. namespace cat {
  35. namespace sphynx {
  36. /*
  37. Designed for server hardware with many processors.
  38. In order to handle many users, the Sphynx server opens up a single UDP port
  39. to accept new connections, and several other UDP data ports for data.
  40. For retransmissions and detecting link loss due to timeout, the server runs
  41. several additional threads that wake up periodically to perform timed tasks.
  42. Server uses thread pool to receive packets on connect and worker ports,
  43. meaning that packets are processed by any free CPU as soon as they arrive.
  44. Sphynx Server
  45. UDP Hello Port [1]
  46. + In case this thread spins constantly, only use one CPU for new
  47. connections since in-game experience is more important than login
  48. + Assigns users to a data port after handshake completes
  49. UDP Data Ports [4 * (CPU Count)]
  50. + Spread users evenly across several ports since
  51. only one packet can be processed from a single port at a time
  52. + Any free CPU will process incoming packets as fast as possible
  53. ServerTimer threads [(CPU Count) / 2]
  54. + In case these threads spin constantly, they only consume
  55. half of the CPU resources available
  56. + Wake up every X milliseconds according to Transport::TICK_RATE
  57. + Detect link loss due to silence timeout
  58. + Update transport layer
  59. + Retransmit lost messages
  60. + Re-evaluate bandwidth and transmit queued messages
  61. */
  62. //// sphynx::Connexion
  63. // Derive from sphynx::Connexion and sphynx::Server to define server behavior
  64. class Connexion : public Transport, public ThreadRefObject
  65. {
  66. friend class Server;
  67. friend class Map;
  68. friend class ServerWorker;
  69. friend class ServerTimer;
  70. public:
  71. Connexion();
  72. virtual ~Connexion() {}
  73. private:
  74. volatile u32 _destroyed;
  75. u32 _key; // Map hash table index, unique for each active connection
  76. Connexion *_next_delete;
  77. ServerWorker *_server_worker;
  78. u8 _first_challenge[64]; // First challenge seen from this client address
  79. u8 _cached_answer[128]; // Cached answer to this first challenge, to avoid eating server CPU time
  80. private:
  81. // Return false to destroy this object
  82. bool Tick(ThreadPoolLocalStorage *tls, u32 now);
  83. void OnRawData(ThreadPoolLocalStorage *tls, u8 *data, u32 bytes);
  84. virtual bool PostPacket(u8 *buffer, u32 buf_bytes, u32 msg_bytes, u32 skip_bytes);
  85. public:
  86. CAT_INLINE bool IsValid() { return _destroyed == 0; }
  87. CAT_INLINE u32 GetKey() { return _key; }
  88. void Destroy();
  89. protected:
  90. NetAddr _client_addr;
  91. // Last time a packet was received from this user -- for disconnect timeouts
  92. u32 _last_recv_tsc;
  93. bool _seen_encrypted;
  94. AuthenticatedEncryption _auth_enc;
  95. protected:
  96. virtual void OnConnect(ThreadPoolLocalStorage *tls) = 0;
  97. virtual void OnDestroy() = 0;
  98. virtual void OnTick(ThreadPoolLocalStorage *tls, u32 now) = 0;
  99. };
  100. //// sphynx::Map
  101. class Map
  102. {
  103. protected:
  104. CAT_INLINE u32 hash_addr(const NetAddr &addr, u32 salt);
  105. public:
  106. struct Slot
  107. {
  108. Connexion *connection;
  109. bool collision;
  110. Slot *next;
  111. };
  112. protected:
  113. u32 _hash_salt;
  114. CAT_ALIGNED(16) Slot _table[HASH_TABLE_SIZE];
  115. RWLock _table_lock;
  116. public:
  117. Map();
  118. virtual ~Map();
  119. // Lookup client by address
  120. Connexion *Lookup(const NetAddr &addr);
  121. // Lookup client by key
  122. Connexion *Lookup(u32 key);
  123. // May return false if network address in Connexion is already in the map.
  124. // This averts a potential race condition but should never happen during
  125. // normal operation, so the Connexion allocation by caller won't be wasted.
  126. bool Insert(Connexion *conn);
  127. // Destroy a list described by the 'next' member of Slot
  128. void DestroyList(Map::Slot *kill_list);
  129. void Tick(ThreadPoolLocalStorage *tls);
  130. };
  131. //// sphynx::ServerWorker
  132. class ServerWorker : public UDPEndpoint
  133. {
  134. friend class Map;
  135. protected:
  136. Map *_conn_map;
  137. ServerTimer *_server_timer;
  138. protected:
  139. volatile u32 _session_count;
  140. public:
  141. ServerWorker(Map *conn_map, ServerTimer *server_timer);
  142. virtual ~ServerWorker();
  143. void IncrementPopulation();
  144. void DecrementPopulation();
  145. u32 GetPopulation() { return _session_count; }
  146. protected:
  147. void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
  148. void OnWrite(u32 bytes) {}
  149. void OnClose();
  150. };
  151. //// sphynx::ServerTimer
  152. class ServerTimer : Thread
  153. {
  154. protected:
  155. Map *_conn_map;
  156. protected:
  157. ServerWorker **_workers;
  158. int _worker_count;
  159. protected:
  160. Map::Slot *_insert_head;
  161. Mutex _insert_lock;
  162. protected:
  163. Map::Slot *_active_head;
  164. public:
  165. ServerTimer(Map *conn_map, ServerWorker **workers, int worker_count);
  166. virtual ~ServerTimer();
  167. CAT_INLINE bool Valid() { return _worker_count > 0; }
  168. public:
  169. void InsertSlot(Map::Slot *slot);
  170. protected:
  171. CAT_INLINE void Tick(ThreadPoolLocalStorage *tls);
  172. bool ThreadFunction(void *param);
  173. protected:
  174. static const int TIMER_THREAD_KILL_TIMEOUT = 10000;
  175. WaitableFlag _kill_flag;
  176. };
  177. //// sphynx::Server
  178. class Server : public UDPEndpoint
  179. {
  180. public:
  181. Server();
  182. virtual ~Server();
  183. bool StartServer(ThreadPoolLocalStorage *tls, Port port, u8 *public_key, int public_bytes, u8 *private_key, int private_bytes, const char *session_key);
  184. u32 GetTotalPopulation();
  185. static bool GenerateKeyPair(ThreadPoolLocalStorage *tls, const char *public_key_file,
  186. const char *private_key_file, u8 *public_key,
  187. int public_bytes, u8 *private_key, int private_bytes);
  188. private:
  189. static const int SESSION_KEY_BYTES = 32;
  190. char _session_key[SESSION_KEY_BYTES];
  191. Port _server_port;
  192. Map _conn_map;
  193. CookieJar _cookie_jar;
  194. KeyAgreementResponder _key_agreement_responder;
  195. u8 _public_key[PUBLIC_KEY_BYTES];
  196. static const int WORKER_LIMIT = 32; // Maximum number of workers
  197. ServerWorker **_workers;
  198. int _worker_count;
  199. ServerTimer **_timers;
  200. int _timer_count;
  201. private:
  202. ServerWorker *FindLeastPopulatedPort();
  203. void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
  204. void OnWrite(u32 bytes);
  205. void OnClose();
  206. protected:
  207. // Must return a new instance of your Connexion derivation
  208. virtual Connexion *NewConnexion() = 0;
  209. // IP address filter: Return true to allow the connection to be made
  210. virtual bool AcceptNewConnexion(const NetAddr &src) = 0;
  211. // Lookup client by key
  212. Connexion *Lookup(u32 key);
  213. };
  214. } // namespace sphynx
  215. } // namespace cat
  216. #endif // CAT_SPHYNX_SERVER_HPP
粤ICP备19079148号