C++ 网络程序设计:Boost Asio、序列化和 OStream

Posted

技术标签:

【中文标题】C++ 网络程序设计:Boost Asio、序列化和 OStream【英文标题】:C++ Networked Program Design: Boost Asio, Serialization, and OStream 【发布时间】:2014-10-16 15:51:26 【问题描述】:

背景信息:

我开始为我正在进行的一个小型演示项目学习网络。我有一个服务器,其中包含一堆具有各种参数(大小、颜色、速度、加速度等)的 Ball 对象。我希望服务器能够做两件事

    将所有参数发送到客户端,以便客户端可以创建一个与服务器上完全相同的新 Ball 对象。 能够定期发送关于球的较小更新,这些更新只会改变它的一些参数(通常是位置和速度)。我们的想法是不要重复发送信息。

我对如何处理这个问题有点不知所措,因为要处理的事情太多了。我的想法是创建一个名为 ClientUpdate 的类,它是我可能想要发送的特定更新类型的抽象基类。

class ClientUpdate

protected:
    UpdateTypes type;
public:
    ClientUpdate();
    void setType(UpdateTypes t) type = t; 
    virtual void print(ostream& where)const;
    friend std::ostream& operator<<(std::ostream& os, const ClientUpdate & obj)
    
        obj.print(os);
        return os;
    
;

然后对于服务器上可能发生的每个事件,例如当球改变颜色或将其状态从冻结变为未冻结时,我将创建 ClientUpdate 的子类来描述事件。子类将具有简单的变量(字符串、整数、布尔值),我将使用 print 函数将其写入 ostream。

最后,我将在每个更新周期中存储在我的游戏的某个区域(例如房间)发生的所有更新,然后对于订阅该区域的任何客户端,我会发送 1 个大字节客户端更新数组,格式为 UPDATETYPE_DATA_UPDATETYPE_DATA_....等。客户端将解析输入流并从中重新创建更新类(我还没有编写此代码,但我认为这不会很困难)。

我正在使用 Boost::Asio 作为网络代码,我正在关注这里的教程:http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio/?pg=10。我之所以提到这一点,是因为我很确定我想坚持使用 boost asio,因为我试图对 boost 和现代 c++ 感到非常舒服。


问题:

(1) 基本问题是“这是解决我的问题的合理方法吗?”我非常有信心至少可以让它工作,但作为任何网络相关的新手,我不确定我是否在重新发明***或在有更简单的做事方式时浪费时间。特别是,将所有“更新”对象收集在一起并通过 1 次大写入发送它们是否效率低下,或者我应该通过单独的写入将单个更新发送到套接字?

(2) 例如,我读过关于 Boost::Serialize 的文章,它似乎与我正在做的非常相似。但是,我对更新在客户端和服务器上应该几乎相同的对象的某些成员变量更感兴趣。 Boost::serialize 对这个有好处,还是更适合发送整个对象?是否还有其他库可以做与我所描述的类似的事情?

【问题讨论】:

【参考方案1】:

从这里很难判断取舍。

我可以看到一些方法(免责声明,我并没有试图详尽无遗,只是大声思考):

    游戏状态的每一次突变都是一个“事件”;您“记录”事件,并且每隔一段时间您就会将一批这些事件发送到另一方。另一方应用它们并发回校验和,验证结果状态是否与发送方的状态匹配(在发送时)。

    或者,您将整个游戏状态视为“文档”。每隔 xxx 毫秒,你对游戏状态进行一次快照,并将其发送给对方。另一方将其游戏状态替换为文档中的游戏状态。服务器可以通过将游戏状态与前者不同(通过保存先前发送的快照)并仅发送增量来优化带宽。

    在最后一个方面,可能与第一种方法相似,但有一个根本的区别:在第一种方法中,发送到另一端的突变与它们发生时完全相同在源系统上;在第二种方法中,“delta”突变是综合从有效差异到最后一个快照:它们与实际导致当前游戏状态的事件序列无关。

现在,权衡取舍很多,取决于以下因素:

完整的游戏状态有多大(国际象棋棋盘被简单地编码为几个字节,3D 射击游戏无法发送完整的快照,甚至可能无法保留快照以供求差) 那里有多少个球,它们是如何存储的;如果它们在基于节点的数据结构中,替换整个游戏状态可能会变得昂贵(因为可能有很多分配)。 有多少不同的状态突变(命令语言会变得多么复杂;为期刊设计“命令语言”是否有意义,还是会变得过于复杂?) 每秒将发生多少事件(触发器的数量是否仅基于输入?例如,在国际象棋中,每 n 秒会有一次移动,但在平衡游戏中可能有数百个每秒输入,如果不是更多)。

等等。所有这些问题都会使某些方法更具吸引力,而另一些则不那么有吸引力。


您没有解决的一个关键问题是:双方都会有“输入”吗?如果是这样,是否存在冲突的输入?如果稍晚收到来自另一方的输入,是否会导致一方的变化导致不同的结果?

我暂时不会讨论这个。如果您需要双向同步,您将变得非常依赖低延迟和频繁更新,这样您就可以在差异变得明显和烦人之前纠正不同的游戏状态。


我也不会讨论如何你应该发送数据,因为它在很大程度上取决于选择的方法。如果您发送完整的文档,正如您所注意到的,Boost Serialization 看起来是一个不错的选择。

【讨论】:

以上是关于C++ 网络程序设计:Boost Asio、序列化和 OStream的主要内容,如果未能解决你的问题,请参考以下文章

Boost.Asio c++ 网络编程翻译

boost::asio基本使用

boost::asio::ip::tcp实现网络通信的小例子

asio核心概念和功能

使用谷物序列化和boost :: asio的例子?

在 boost 中序列化二进制数据失败并出现“无效签名”错误