4

我正在设计一个游戏服务器,我以前从未做过这样的事情。我只是想知道数据包的好结构是什么?如果重要的话,我正在使用 TCP。这是一个示例,以及我目前正在考虑使用的示例:

(括号中的每个值都是一个字节)

[Packet length][Action ID][Number of Parameters]
[Parameter 1 data length as int][Parameter 1 data type][Parameter 1 data (multi byte)]
[Parameter 2 data length as int][Parameter 2 data type][Parameter 2 data (multi byte)]
[Parameter n data length as int][Parameter n data type][Parameter n data (multi byte)]

就像我说的那样,我以前真的从来没有做过这样的事情,所以我上面所说的可能是完全的公牛,这就是我问的原因;)。此外,是否有必要传递总数据包长度?

4

5 回答 5

3

传递总数据包长度是个好主意。它可能会多花两个字节,但您可以窥视并等待套接字在接收之前准备好完整的数据包。这使代码更容易。

总的来说,我同意 brazzy 的观点,一种语言提供的序列化机制比任何自制的更可取。

除此之外(我认为您使用的是没有序列化的 C-ish 语言),我会将数据包 ID 作为数据包数据结构上的第一个数据。恕我直言,这是某种约定,因为结构的第一个数据成员始终位于位置 0,并且任何结构都可以向下转换为该位置,从而识别其他匿名数据。

您的编译器可能会或可能不会生成打包结构,但这样您就可以分配缓冲区,读取数据包,然后根据第一个数据成员转换结构。如果你不走运并且它不产生压缩结构,请确保每个结构都有一个序列化方法,它将从(显然是非目标)内存构造。

Endiannes 是一个因素,尤其是在类 C 语言中。请务必明确数据包始终具有相同的字节序,或者您可以根据签名或其他内容识别不同的字节序。一件非常酷的奇怪事情:当您使用本文中讨论的方法访问它们时,C# 和 .NET 似乎总是以 little-endian 约定保存数据。在将这样的应用程序移植到 SUN 上的 Mono 时发现了这一点。很酷,但是如果你有那个设置,你应该使用 C# 的序列化方法。

除此之外,您的设置看起来非常好!

于 2008-12-27T11:37:42.373 回答
3

首先考虑一个更简单的基本包装器:标签、长度、值(TLV)。您的基本数据包将如下所示:

[Tag] [Length] [Value]

Tag是数据包标识符(如您的操作 ID)。

长度是数据包长度。您可能需要它来判断您是否拥有完整的数据包。它还可以让您计算出价值部分的长度。

包含实际数据。这个格式可以是任何东西。

在上述情况下,值数据包含进一步的一系列 TLV 结构(参数类型、长度、值)。您实际上不需要发送参数的数量,因为您可以根据数据长度和遍历数据来处理它。

正如其他人所说,我会将数据包ID(标签)放在第一位。除非您有跨平台问题,否则我会考虑将应用程序的序列化对象包装在 TLV 中,然后像这样通过网络发送它。如果您出错或想稍后更改,您始终可以创建具有不同结构的新标签。

有关TLV的更多详细信息,请参阅 Wikipedia 。

于 2008-12-27T12:46:26.633 回答
3

为避免重新发明轮子,任何序列化协议都适用于有线数据(例如 XML、JSON),您可能会考虑将BEEP视为基本协议框架。

BEEP 在其常见问题解答文档中得到了很好的总结,它是自 80 年代初以来经验丰富的应用程序协议设计人员使用的技巧的“最佳热门”专辑。'

于 2008-12-27T13:38:58.150 回答
2

没有理由做这么复杂的事情。我看到您有一个操作 ID,所以我想会有固定数量的操作。

对于每个操作,您将定义一个数据结构,然后将这些值中的每一个放入该结构中。要通过网络发送它,您只需为结构中的每个元素分配 sum(sizeof(struct.i)) 字节。所以你的数据包看起来像这样:

[action ID][item 1 (sizeof(item 1 bytes)][item 1 (sizeof(item 2 bytes)]...[item n (sizeof(item n bytes)]

这个想法是,您已经知道连接每一侧的每个变量的大小和类型,因此您不需要发送该信息。

对于字符串,您可以将它们以空终止的形式放入,然后当您“知道”根据您的数据包类型查找字符串时,开始读取并查找空值。

--

另一种选择是使用 '\r\n' 来描述你的变量。这将需要一些开销,并且您必须使用文本,而不是数字的二进制值。但是这样你就可以使用 readline 来读取每个变量。你的数据包看起来像这样

[action ID]
[item 1 (as text)]
...
[item n (as text)]

--

最后,简单地序列化对象并将它们传递到网络中也是一种很好的方法,而且要编写的代码量最少。请记住,您不想过早地进行优化,这也包括网络流量。如果事实证明您需要稍后挤出更多性能,您可以返回并找出更有效的机制。

并查看 google 的协议缓冲区,它被认为是一种以平台无关的方式序列化数据的极快方式,有点像二进制 XML,但没有嵌套元素。还有JSON,这是另一种平台中性编码。使用协议缓冲区或 JSON 意味着您不必担心如何专门对消息进行编码。

于 2008-12-27T11:44:42.903 回答
0

您希望服务器支持用不同语言编写的多个客户端吗?如果不是,则可能没有必要准确指定结构;而是使用您的语言提供的任何工具来序列化数据,只是为了减少出错的可能性。

如果您确实需要结构是可移植的,那么上面看起来还可以,尽管在这种情况下您也应该指定字节顺序和文本编码等内容。

于 2008-12-27T11:26:50.023 回答