基于ACE设计一个c++网络游戏服务器框架引擎

          利用闲暇时间,我正在开发一个网络游戏服务器引擎,取名叫gabriel, 代表圣经里的一个天使,中文名叫加百列。加百列在圣经中是一个大天使长,他负责将上 帝的话语带到世间,并晓谕诸位先知,帮助世人明白上帝的旨意。之所以取这个名字,一方面是向大家推荐《圣经》这本宝书(也算是传福音吧),另一方面也希望这个框架引擎能够起到抛砖引玉的作用,引起同行或是打算从事游戏服务器端开发的朋友的共同探讨,以求共同进步。

        在c++网络库里面,ACE框架库算是历史悠久且知名度最高的了,ACE不单是提供网络功能,它实际上是一揽子解决方案,包括内存管理、定时器、并发机制、日志系统等。我也比较认同ACE的理念,将低级api函数用c++类来封装,隔离复杂且易出错的api调用链,并抽象出网络通信常见模型。虽然gabriel引擎还未完全实现,基本结构已初步确定下来了,代码寄放在github上,网址是 https://github.com/lichuan/gabriel

       说实话,我一直觉得makefile的语法太丑了,所以本项目未采用makefile的方式构建,而是用python写成的scons来构建。关于scons的使用说明请参阅官方网站,项目目录说明如下:

       doc目录存放的是文档(暂时只有一个我总结的c++编码规范),protocol目录存放的是通信协议,基于protobuf,引擎源码则存放于src目录。一个有6种服务器,分别是supercenter, center, login, record, game, gateway,各个服务器功能分配如下:

       supercenter服务器是超级中心服务器,管理每一个区的center服务器。

       center服务器是一个区的中心服务器,负责管理区内的其他服务器。

       login服务器是登陆服务器,用于玩家登陆验证等功能。

       record服务器是档案服务器,用于存储游戏中的数据以及玩家的信息。

       game服务器是游戏主逻辑服务器,负责实现游戏里的各种玩法,各种活动。

       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类也是属于基类,因为存在两种不同的连接通道,即Client_Connection和Server_Connection类,这两个类怎么理解呢?假如a服务器主动连接到b服务器,那么对于a服务器来说这条通道称为Server_Connection,因为是到其他服务器的连接,但是对于b服务器来说,这条通道称为Client_Connection,因为是其他服务器连接到b服务器的,相当于只是b服务器的一条客户连接,这跟玩家连接到b服务器,玩家到b服务器的这条连接也是称之为b服务器的Client_Connection是一样的概念。Server_Connection主要是用于服务器之间通信时主动连接方所采用的概念。文件server_connection.hpp和client_connection.hpp定义了这两种类型的连接,当然它们都继承于Connection类。

        有了上面的认识后,那么我们再看看各个具体的服务器到底是如何定义的呢?就拿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 SERVER;

#endif

        显然,gabriel::game::Server类是继承于base命名空间中的基类,这里面重写了这个服务器子类对于基类中感兴趣的虚函数,此外,还定义了两个数据成员m_center_connection和m_record_connection,类型都是Server_Connection,容易看出,这代表game服务器到存档服务器和中心服务器的连接通道,因为game服务器需要保存数据到record,也需要注册自己到center服务器中。最后,看看系统的启动函数,位于server.cpp中:

int ACE_MAIN (int argc, char *argv[])
{    
    SERVER::instance()->main();

    return 0;
}

        ACE_MAIN其实是ACE中的宏,包装了一下main函数,并且多加了一些处理而已,启动函数里面的内容相当简洁,就只有一行调用: SERVER::instance()->main(); 这里会执行Server基类中的main()函数来启动服务器。

目前为止,引擎具备的是一个完整的框架结构,还有一些具体的内容需要填充,在这里我也欢迎大家参与进来,共同开发,我本人也会利用空闲时间继续开发该引擎,如果你对上面的介绍不太能理解的话,欢迎到github上阅读代码吧,也欢迎你亲自pull下来编译运行,加深理解哈。如果你有什么建议或意见,请不吝赐教!

你可能感兴趣的:(c++,程序设计,编码相关)