ACEに基づいてc++ネットワークゲームサーバフレームワークエンジンを設計する


暇な時間を利用して、私はネットゲームサーバーのエンジンを開発しています.gabrielという名前で、聖書の天使を代表しています.中国語の名前はガブリエルです.ガブリエルは聖書の中で大天使長で,神の言葉をこの世に持ち込み,預言者たちに神の意図を理解させるのを助けた.この名前をつけたのは、聖書という宝書を推薦する一方で、このフレームワークエンジンがレンガを投げて玉を引く役割を果たし、同業者やゲームサーバーの開発に従事しようとする友人の共同検討を引き起こし、共同の進歩を図ることを望んでいる.
c++ネットワークライブラリの中で、ACEフレームワークライブラリは歴史が古く、知名度が最も高く、ACEはネットワーク機能を提供するだけでなく、メモリ管理、タイマ、同時メカニズム、ログシステムなどを含む包括的なソリューションです.私もACEの理念を比較的に認めて、低級api関数をc++クラスでカプセル化して、複雑で間違いやすいapi呼び出しチェーンを隔離して、そしてネットワーク通信のよくあるモデルを抽象化します.gabrielエンジンはまだ完全に実現されていませんが、基本構造は初歩的に確定されています.コードはgithubに預けられています.ウェブサイトはhttps://github.com/lichuan/gabriel
正直、私はずっとmakefileの文法が醜いと思っていたので、本プロジェクトはmakefileではなくpythonで書かれたsconsで構築しました.sconsの使用方法については、公式サイトを参照してください.プロジェクトディレクトリの説明は次のとおりです.
docディレクトリにはドキュメント(私がまとめたc++符号化仕様は一時的に1つしかありません)、protocolディレクトリには通信プロトコルが格納され、protobufに基づいてエンジンソースコードはsrcディレクトリに格納されます.1つには、supercenter、center、login、record、game、gatewayの6種類のサーバがあり、各サーバの機能は以下のように割り当てられています.
supercenterサーバはスーパーセンターサーバであり、各ゾーンのcenterサーバを管理します.
centerサーバは、ゾーン内の他のサーバを管理するゾーンの中心サーバです.
loginサーバはログインサーバであり,プレイヤーのログイン検証などの機能に用いられる.
recordサーバは、ゲーム中のデータやプレイヤーの情報を格納するアーカイブサーバです.
ゲームサーバはゲームのメインロジックサーバで、ゲーム内の様々な遊び方、様々な活動を実現します.
gatewayサーバはゲートウェイサーバであり、プレイヤーは直接このサーバに接続し、ゲームのメインロジックサーバとプレイヤーの間で安全防護柵を隔離することに相当する.
「簡潔は美」というのは、私が非常に賛成した言葉です.私たちが書いたコードが他の人を楽に理解させることができれば、このようなコードは美しいコードです.上記のサーバタイプは全部で6種類あり、各サーバが単独で記述するとコードの重複冗長性をもたらす必要があるため、それらの共通性を抽出して単独のベースクラスを形成し、src目にbaseディレクトリを録画し、serverを使用する必要があると考えてみましょう.hpp、これはすべてのサーバのベースクラスであり、クラス定義は以下の通りです.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *      _____       ___   _____   _____    _   _____   _               *
 *     /  ___|     /   | |  _  \ |  _  \  | | | ____| | |              *
 *     | |        / /| | | |_| | | |_| |  | | | |__   | |              *
 *     | |  _    / / | | |  _  { |  _  /  | | |  __|  | |              *
 *     | |_| |  / /  | | | |_| | | | \ \  | | | |___  | |___           *
 *     \_____/ /_/   |_| |_____/ |_|  \_\ |_| |_____| |_____|          *
 *                                                                     *
 *     gabriel is an angel from the Holy Bible, this engine is named   *
 *   gabriel, means bringing people good news. the goal of gabriel     *
 *   server engine is to help people to develop various online games,  *
 *   welcome you to join in.                                           *
 *                                                                     *
 *   @author: lichuan                                                  *
 *   @qq: 308831759                                                    *
 *   @email: [email protected]                                          *
 *   @site: www.lichuan.me                                             *
 *   @github: https://github.com/lichuan/gabriel                       *
 *   @date: 2013-11-29 09:00:34                                        *
 *                                                                     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef GABRIEL__BASE__SERVER
#define GABRIEL__BASE__SERVER

#include "ace/SOCK_Acceptor.h"
#include "ace/SOCK_Connector.h"
#include "gabriel/base/connector.hpp"
#include "gabriel/base/acceptor.hpp"
#include "gabriel/base/thread.hpp"
#include "gabriel/base/client_connection.hpp"
#include "gabriel/base/server_connection.hpp"

namespace gabriel {
namespace base {
    
class Server : public Entity_Manager
{
public:
    Server();
    virtual ~Server();
    void add_connection(Client_Connection *client_connection);
    virtual bool verify_connection(Client_Connection *client_connection);
    void main();
    uint32 state() const;
    void state(uint32 _state);
    virtual void on_connection_shutdown(Server_Connection *server_connection);
    virtual void on_connection_shutdown(Client_Connection *client_connection);
    virtual void handle_client_connection_msg(Client_Connection *client_connection, uint32 msg_type, uint32 msg_id, void *data, uint32 size);
    virtual void register_msg_handler() = 0;
    
protected:
    void register_client_connection_msg_handler(uint32 msg_type, uint32 msg_id, void (*handler)(Client_Connection *client_connection, void *data, uint32 size));
    Gabriel_Acceptor m_acceptor;
    Gabriel_Connector m_connector;
    
private:    
    int32 init();
    void fini();
    void run();
    void do_reactor();
    void do_decode();
    void do_encode();
    void do_main();
    void do_main_client_connection();
    virtual void do_decode_server_connection();
    virtual void do_encode_server_connection();
    virtual void do_main_server_connection();    
    virtual int32 init_hook() = 0;
    virtual void update();
    virtual void fini_hook();
    ID_Allocator<> m_connection_id_allocator;
    uint32 m_state;
    Thread m_thread;
    Message_Handler m_client_connection_msg_handler;
};

}
}

#endif

ベースクラスには接続管理、メッセージ処理などのインタフェースが定義され、do_先頭のいくつかの関数は、符号化、復号化などの個別のスレッドで実行されることを示します.do_mainはメインロジックスレッドを表し、すべてのゲームロジック業務はこのスレッドで実行され、クラス内のmain()関数は起動関数を表し、このような処理はサブクラスコードをより簡潔に一致させ、理解しやすいようにするためである.init_hookとfini_hookは、サブクラスの初期化および終了時の操作を実行するためのフック関数です.
サーバとサーバの間またはプレイヤーとサーバの間に接続チャネルを確立する必要があり、この通信チャネルをConnectionクラス、ファイルconnectionに抽象化する.hppで定義:
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *      _____       ___   _____   _____    _   _____   _               *
 *     /  ___|     /   | |  _  \ |  _  \  | | | ____| | |              *
 *     | |        / /| | | |_| | | |_| |  | | | |__   | |              *
 *     | |  _    / / | | |  _  { |  _  /  | | |  __|  | |              *
 *     | |_| |  / /  | | | |_| | | | \ \  | | | |___  | |___           *
 *     \_____/ /_/   |_| |_____/ |_|  \_\ |_| |_____| |_____|          *
 *                                                                     *
 *     gabriel is an angel from the Holy Bible, this engine is named   *
 *   gabriel, means bringing people good news. the goal of gabriel     *
 *   server engine is to help people to develop various online games,  *
 *   welcome you to join in.                                           *
 *                                                                     *
 *   @author: lichuan                                                  *
 *   @qq: 308831759                                                    *
 *   @email: [email protected]                                          *
 *   @site: www.lichuan.me                                             *
 *   @github: https://github.com/lichuan/gabriel                       *
 *   @date: 2013-11-29 09:00:07                                        *
 *                                                                     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef GABRIEL__BASE__CONNECTION
#define GABRIEL__BASE__CONNECTION

#include "ace/Svc_Handler.h"
#include "ace/SOCK_Stream.h"
#include "gabriel/base/common.hpp"
#include "gabriel/base/entity.hpp"

namespace gabriel {
namespace base {
    
class Server;
    
class Connection : public ACE_Svc_Handler, public Entity<>
{
    typedef ACE_Svc_Handler Super;    
public:
    Connection();
    virtual ~Connection();    
    virtual int open(void *acceptor_or_connector);
    virtual int handle_input(ACE_HANDLE hd = ACE_INVALID_HANDLE);
    virtual int handle_output(ACE_HANDLE hd = ACE_INVALID_HANDLE);
    uint32 state() const;
    void state(uint32 _state);
    bool connected() const;
    void decode();
    void encode();
    void shutdown();
    void dispatch();    
    void send(uint32 msg_type, uint32 msg_id, void *data, uint32 size);
    void do_main();
    
protected:
    Server *m_holder;

private:
    virtual void dispatch(uint32 msg_type, uint32 msg_id, void *data, uint32 size) = 0;
    virtual void on_shutdown() = 0;    
    uint32 decode_msg_length();
    ACE_Message_Queue m_recv_queue;
    ACE_Message_Queue m_send_queue_1;
    ACE_Message_Queue m_send_queue_2;
    uint32 m_state;
    bool m_cancel_write;
    uint32 m_last_decode_msg_length;
};
    
}
}

#endif

このConnectionクラスもベースクラスに属します.2つの異なる接続チャネル、すなわちClient_が存在するためです.接続とサーバ_Connectionクラス、この2つのクラスはどのように理解しますか?aサーバがbサーバにアクティブに接続されている場合、aサーバにとってこのチャネルはサーバと呼ばれます.Connectionは、他のサーバへの接続ですが、bサーバにとってこのチャネルはClient_と呼ばれます.Connectionは、他のサーバがbサーバに接続されているため、bサーバの1つのクライアント接続に相当し、これはプレイヤーとbサーバに接続され、プレイヤーからbサーバへのこの接続もbサーバと呼ばれるClient_Connectionは同じ概念です.Server_Connectionは主にサーバ間の通信時にアクティブな接続先に用いられる概念である.ファイルserver_connection.hppとclient_connection.hppは、これら2つのタイプの接続を定義します.もちろん、接続クラスに継承されます.
上記の認識があれば、各具体的なサーバがどのように定義されているのかを見てみましょう.gameサーバについて言えば、srcディレクトリの下にgameディレクトリがあります.ここではgameプライマリロジックサーバに関する定義を定義します.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *      _____       ___   _____   _____    _   _____   _               *
 *     /  ___|     /   | |  _  \ |  _  \  | | | ____| | |              *
 *     | |        / /| | | |_| | | |_| |  | | | |__   | |              *
 *     | |  _    / / | | |  _  { |  _  /  | | |  __|  | |              *
 *     | |_| |  / /  | | | |_| | | | \ \  | | | |___  | |___           *
 *     \_____/ /_/   |_| |_____/ |_|  \_\ |_| |_____| |_____|          *
 *                                                                     *
 *     gabriel is an angel from the Holy Bible, this engine is named   *
 *   gabriel, means bringing people good news. the goal of gabriel     *
 *   server engine is to help people to develop various online games,  *
 *   welcome you to join in.                                           *
 *                                                                     *
 *   @author: lichuan                                                  *
 *   @qq: 308831759                                                    *
 *   @email: [email protected]                                          *
 *   @site: www.lichuan.me                                             *
 *   @github: https://github.com/lichuan/gabriel                       *
 *   @date: 2014-01-09 12:40:31                                        *
 *                                                                     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef GABRIEL__GAME__SERVER
#define GABRIEL__GAME__SERVER

#include "gabriel/base/server.hpp"
#include "gabriel/base/message_handler.hpp"

namespace gabriel {
namespace game {

class Server : public gabriel::base::Server
{
public:
    Server();
    virtual ~Server();
    
private:
    virtual void on_connection_shutdown(gabriel::base::Client_Connection *client_connection);
    virtual void on_connection_shutdown(gabriel::base::Server_Connection *server_connection);
    virtual bool verify_connection(gabriel::base::Client_Connection *client_connection);
    virtual void do_decode_server_connection();
    virtual void do_encode_server_connection();
    virtual void do_main_server_connection();
    virtual void update();
    virtual int32 init_hook();
    virtual void fini_hook();
    virtual void register_msg_handler();
    gabriel::base::Server_Connection m_center_connection;
    gabriel::base::Server_Connection m_record_connection;
};
    
}
}

typedef ACE_Singleton<:game::server ace_null_mutex=""> SERVER;

#endif

明らかに、gabriel::game::Serverクラスはbaseネーミングスペースに継承されたベースクラスであり、このサーバサブクラスがベースクラスに興味のある虚関数を書き換えたほか、2つのデータメンバーm_も定義されている.center_接続とm_record_接続、タイプはすべてServer_Connectionでは、gameサーバからアーカイブサーバとセンターサーバへの接続チャネルを表します.gameサーバはrecordにデータを保存する必要があり、centerサーバに自分を登録する必要があるからです.最後に、serverにあるシステムの起動関数を見てみましょう.cpp:
int ACE_MAIN (int argc, char *argv[])
{    
    SERVER::instance()->main();

    return 0;
}

        ACE_MAINは実はACEの中のマクロで、main関数を包装して、しかもいくつかの処理を加えただけで、起動関数の中の内容はかなり簡潔で、1行の呼び出ししかありません:
SERVER::instance()->main(); サーバを起動するには、サーバベースクラスのmain()関数を実行します.
これまで、エンジンは完全なフレームワーク構造を備えており、具体的な内容を記入する必要があります.ここでも皆さんの参加を歓迎します.共同開発を歓迎します.私自身も暇な時間を利用してエンジンを開発し続けます.上記の紹介があまり理解できない場合は、githubでコードを読むことを歓迎します.自分でpullしてコンパイルして実行することを歓迎します.理解を深めるね.何かアドバイスや意見があれば、教えてください.