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

C++ Networked Program Design: Boost Asio, Serialization, and OStream

本文关键字:Serialization OStream Asio Boost 网络 程序设计 C++      更新时间:2023-10-16

背景信息:

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

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

我有点不知所措,因为有太多事情要处理。我的想法是创建一个名为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. 游戏状态的每一个突变都是一个"事件";你"记录"事件,每隔一段时间就会向对方发送一批事件。另一方应用它们,并发回一个校验和,验证结果状态是否与发送方(发送时)的状态匹配。

  2. 或者,您将整个游戏状态视为一个"文档"。每隔xxx毫秒,您就会对游戏状态进行快照,并将其发送给另一方。另一方将其游戏状态替换为文档中的状态。服务器可以通过将游戏状态与前者进行差异(通过保存之前发送的快照)并仅发送增量来优化带宽。

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

现在,权衡是丰富的,取决于以下因素:

  • 完整的游戏状态有多大(棋盘被琐碎地用几个字节编码,3D射击运动员无法发送完整的快照,甚至可能无法保存快照进行差异化)
  • 有多少球,它们是如何储存的;如果它们处于基于节点的数据结构中,替换整个游戏状态可能会变得昂贵(因为可能有很多分配)
  • 有多少不同的状态突变(命令语言会变得多么复杂;为期刊设计一种"命令语言"有意义吗,还是会变得过于复杂?)
  • 每秒会发生多少个事件(触发器的数量是否完全基于输入?例如,在国际象棋中,每n秒就会有一次移动,但在平衡游戏中,每秒可能会有数百次输入,如果不是更多的话)

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


你没有解决的一个关键问题是:双方都会有"投入"吗?如果是的话,是否会有相互冲突的输入?如果稍微晚一点收到另一方的输入,一方的变化会导致不同的结果吗?

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


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