【问题标题】:Transfer objects on per field basis over network通过网络按字段传输对象
【发布时间】:2011-12-04 16:50:06
【问题描述】:

我需要通过网络(多人游戏)传输 .NET 对象(具有层次结构)。为了节省带宽,我想只传输更改的字段(和/或属性),因此不会更改的字段不会传输。

我还需要一些机制来匹配另一客户端上的正确对象(全局对象标识符......像对象 ID 之类的东西?)

我需要一些建议。
你会使用反射吗? (性能至关重要)
我还需要传输 IList 增量(添加对象、删除对象)的机制。

MMO 网络是如何完成的,它们会传输整个对象吗?
(也许我对每个字段转移的想法很愚蠢)

编辑:
说清楚:我已经有了跟踪更改的机制(假设每个字段都有属性,setter 将字段添加到某种列表或字典中,其中包含更改 - 结构现在不是最终的)。

我不知道如何序列化这个列表,然后在其他客户端反序列化它。主要是如何有效地做到这一点以及如何更新适当的对象。

大约有一百个对象,所以我试图避免为每个对象编写特殊函数的情况。用属性装饰字段或属性是可以的(例如指定序列化程序、字段 id 或类似的东西)。

关于对象的更多信息:每个对象平均有 5 个字段。一些对象是从其他对象继承而来的。

谢谢大家的回答。

【问题讨论】:

    标签: c# .net networking serialization multiplayer


    【解决方案1】:

    另一种方法;不要尝试序列化复杂的数据更改:而是只发送 实际命令 来应用(以简洁的形式),例如:

    move 12432 134, 146
    remove 25727
    

    (这将移动 1 个对象并移除另一个对象)。

    然后,您将在接收器上应用命令,如果它们不同步,则允许完全重新同步。

    我不建议您实际为此使用文本 - 这只是为了使示例更清晰。

    这有一个好处:它还免费提供“重播”功能。

    【讨论】:

    • 谢谢你的回答,我没有考虑这个
    【解决方案2】:

    跟踪脏字段最便宜的方法是将其作为对象模型的一个关键特性,即每个数据字段“foo”都有一个“fooDirty”字段,您在“set”中设置为true(如果值不同)。这也可以与条件序列化结合起来,也许是一些序列化程序观察到的“ShouldSerializeFoo()”模式。我不知道有任何库与您所描述的完全匹配(除非我们包括 DataTable,但是......想想小猫!)

    也许另一个问题是在反序列化期间需要跟踪所有对象以进行合并;这本身并不是免费的。

    不过,考虑到所有事情,我认为您可以在上述几行(fooDirty/ShouldSerializeFoo)上做一些事情并使用 protobuf-net 作为序列化程序,因为(重要的是)它支持条件序列化和合并。我还建议使用如下界面:

    ISomeName {
        int Key {get;}
        bool IsDirty {get;}
    }
    

    IsDrty 将允许您快速检查所有对象是否有更改,然后将密钥添加到流中,然后进行(条件)序列化。调用者将读取密钥,获取所需的对象(或使用该密钥分配一个新对象),然后使用启用合并的反序列化(传入现有/新对象)。

    不是一个完整的演练,但如果是我,这就是我要研究的方法。注意:子集合中对象的添加/删除/排序是一个棘手的领域,可能需要考虑。

    【讨论】:

      【解决方案3】:

      我只想说 Marc Gravell 的建议确实是正确的方法。他忽略了一些次要细节,例如冲突解决(您可能想阅读 Leslie Lamport 的工作。他的整个职业生涯基本上都在描述处理分布式系统中解决冲突的不同方法),但是想法是正确的。

      如果您确实想要传输状态快照,而不是状态更改的过程描述,那么我建议您考虑将快照差异构建为前缀树。基本思想是构建对象和字段的层次结构。当您更改一组字段时,它们所具有的任何公共前缀仅包含一次。这可能看起来像:

      world -> player 1 -> lives: 1
      ...               -> points: 1337
      ...               -> location -> X: 100
      ...                           -> Y: 32
      ...   -> player 2 -> lives: 3
      

      (“...”中的所有内容仅传输一次)。

      【讨论】:

        【解决方案4】:

        只传输已更改的字段是不合逻辑的,因为您会浪费时间检测哪些字段已更改,哪些未更改,以及如何在接收方进行重建,这会为您的游戏增加大量延迟并使其成功无法在线播放。

        我建议的解决方案是让您将对象分解到最低限度并快速发送这些小对象。此外,您可以使用压缩来减少带宽使用。

        对于对象 ID,您可以使用静态 ID,该 ID 在您构建新对象时会增加。

        希望这个答案有帮助。

        【讨论】:

        • 跟踪脏字段可能非常便宜,具体取决于您乐于让它“侵入”到对象模型中,并且可以显着节省序列化。
        • 但是接收者如何知道它正在接收哪些字段,除非他做一些额外的工作来识别脏。这就是我的观点,发送方有一个“小”的额外工作,而接收方有一个“小”的额外工作。结合起来,最后真的省那么多钱吗??
        • 接收者通过已经存在的字段标识知道(在我给出的示例中,协议基本上支持以这种方式合并,但作为一个类比:在 XML 中它将是“元素名称”)。鉴于接收方已经必须这样做,接收方的成本正好为零。在发件人处,检查布尔值比实际序列化任何东西要便宜得多 - 所以在大多数情况下,在发件人处它会更便宜,在 webeverythig很脏。
        【解决方案5】:

        您需要手动执行此操作。与手工制作的任何东西相比,自动跟踪对象层次结构中的属性和实例更改将非常缓慢。

        如果您决定尝试一下,我会尝试将您的对象映射到 DataSet 并使用其内置的修改跟踪机制。

        不过,我仍然认为您应该手动执行此操作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-16
          • 2010-11-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多