16

我编写了许多网络系统,并且对网络的工作方式有很好的了解。然而,我总是最终拥有一个数据包接收功能,这是一个巨大的 switch 语句。这开始影响到我了。我宁愿采用一种优雅的面向对象的方式来处理接收数据包,但每次我试图提出一个好的解决方案时,我总是会失败。

例如,假设您有一个网络服务器。它只是在那里等待响应。一个数据包进来,服务器需要验证数据包,然后它需要决定如何处理它。

目前,我一直在通过打开标头中的数据包 ID 来执行此操作,然后进行大量处理每种数据包类型的函数调用。对于复杂的网络系统,这会导致一个单一的 switch 语句,我真的不喜欢这样处理它。我考虑过的一种方法是使用处理程序类的映射。然后我可以将数据包传递给相关的类并处理传入的数据。我遇到的问题是我需要一些方法来用地图“注册”每个数据包处理程序。这意味着,通常,我需要创建类的静态副本,然后在构造函数中将其注册到中央数据包处理程序。虽然这很有效,但它看起来确实是一种不优雅且繁琐的处理方式。

编辑:同样,拥有一个双向工作的好系统也是理想的。即一个类结构,可以轻松处理发送与接收它们相同的数据包类型(显然通过不同的功能)。

谁能指出我处理传入数据包的更好方法?非常感谢链接和有用的信息!

抱歉,如果我没有很好地描述我的问题,因为我无法很好地描述它也是我从未设法提出解决方案的原因。

4

7 回答 7

5

关于处理数据包类型的方式:对我来说,地图是最好的。但是,我会使用普通数组(或向量)而不是地图。如果您从 0 依次枚举数据包类型,它将使访问时间保持不变。

至于班级结构。有一些库已经完成了这项工作:可用的游戏网络协议定义语言和代码生成。例如, Google 的 Protocol Buffer似乎很有希望。它为协议描述中的每条消息生成一个带有 getter、setter、序列化和反序列化例程的存储类。协议描述语言看起来或多或少是丰富的。

于 2011-02-08T20:57:16.587 回答
1

根据我的经验,表驱动解析是最有效的方法。

虽然std::map很好,但我最终还是使用了静态表。std::map不能静态初始化为常量表。它必须在运行时加载。表(结构数组)可以声明为数据并在编译时初始化。我还没有遇到过足够大的表,而线性搜索是一个瓶颈。通常表大小足够小,二进制搜索的开销比线性搜索慢。

为了获得高性能,我将使用消息数据作为表的索引。

于 2011-02-08T20:13:06.993 回答
1

当你在做 OOP 时,你试图将每一件事都表示为一个对象,对吧?所以你的协议消息也变成了对象;您可能会有一个基类YourProtocolMessageBase,它将封装任何消息的行为,并从中继承您的多态专用消息。然后,您只需要一种将每条消息(即每个YourProtocolMessageBase实例)转换为字节串的方法,以及一种进行反转的方法。这种方法称为序列化技术;存在一些基于元编程的实现

Python中的快速示例:

from socket import *
sock = socket(AF_INET6, SOCK_STREAM)
sock.bind(("localhost", 1234))
rsock, addr = sock.accept()

服务器阻塞,为客户端启动另一个实例:

from socket import *
clientsock = socket(AF_INET6, SOCK_STREAM)
clientsock.connect(("localhost", 1234))

现在使用 Python 的内置序列化模块,pickle; 客户:

import pickle
obj = {1: "test", 2: 138, 3: ("foo", "bar")}
clientsock.send(pickle.dumps(obj))

服务器:

>>> import pickle
>>> r = pickle.loads(rsock.recv(1000))
>>> r
{1: 'test', 2: 138, 3: ('foo', 'bar')}

因此,如您所见,我刚刚通过链接本地发送了一个 Python对象。这不是 OOP 吗?

我认为序列化的唯一可能替代方法是维护 bimap IDs ⇔ 类。这看起来真的是不可避免的。

于 2011-02-08T20:13:48.173 回答
1

处理程序实例的映射几乎是处理它的最佳方式。没有什么不雅的。

于 2011-02-08T19:20:24.923 回答
1

您想继续使用相同的数据包网络协议,但在编程中将其转换为对象,对吗?

有几种协议允许您将数据视为编程对象,但您似乎不想更改协议,就像它在应用程序中处理的方式一样。

数据包是否带有诸如“标签”或元数据或任何“id”或“数据类型”之类的东西,允许您映射到特定的对象类?如果是这样,您可以创建一个存储 id 的数组。和匹配的类,并生成一个对象。

于 2011-02-08T21:09:48.873 回答
1

一种更面向对象的处理方式是使用状态模式构建状态机。

处理传入的原始数据是解析状态机提供优雅解决方案的地方(您必须在优雅和性能之间做出选择)

您有一个要处理的数据缓冲区,每个状态都有一个句柄缓冲区方法,该方法解析和处理他的缓冲区部分(如果已经可能)并根据内容设置下一个状态。

如果你想追求性能,你仍然可以使用状态机,但省略了 OO 部分。

于 2011-02-10T10:50:37.633 回答
1

我会使用Flatbuffers和/或Cap'n Proto代码生成器。

于 2019-04-25T07:09:34.063 回答