Init
This commit is contained in:
300
DependentExtensions/cat/net/SphynxServer.hpp
Normal file
300
DependentExtensions/cat/net/SphynxServer.hpp
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
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
|
||||
Reference in New Issue
Block a user