2009-02-25 24 views
6

这是一种关闭other question的分支。如果你喜欢,请阅读它,但没有必要。基本上,我意识到为了在大消息上有效地使用C#的BeginReceive(),我需要(a)首先读取数据包长度,然后读取恰好那么多字节或(b)使用结束符包分隔符。我的问题是,这些协议缓冲区中是否存在这些协议?我还没有使用它们,但通过文档看起来好像没有长度标题或分隔符。如何检测协议缓冲区消息何时完全接收?

如果不是,我该怎么办?我应该只是建立消息,然后前缀/后缀长度标头/ EOP分隔符?

回答

14

您需要在协议中包含大小或结束标记。除了支持任意分解为单独数据包的不确定数据流(并且数据包也可能在传输中溢出)之外,没有什么内置基于流的套接字(TCP/IP)。

一个简单的方法是每个“消息”都有一个固定大小的头,包括协议版本和有效载荷大小以及任何其他固定数据。然后是消息内容(有效载荷)。

可以添加一个消息页脚(固定大小)和一个校验和甚至一个加密签名(取决于您的可靠性/安全性要求)。

知道有效载荷的大小可以让你保持读取的字节数,这将是足够的消息的其余部分(如果读取与少,做是另一回事读了剩余的字节已收到整个消息,直到完成)。

有一个结束消息指示符也可以,但是你需要定义如何处理包含相同的八位位组序列您的邮件...

1

TCP/IP,以及UDP报文包括一些参考它们的大小。 IP header包含一个16位字段,它指定了IP报头的长度和以字节为单位的数据长度。 TCP header包含一个4位字段,用32位字指定TCP头的大小。 UDP header包含一个16位字段,它指定了UDP数据头的长度(以字节为单位)。

这是事情。不管你在C#中使用System.Net.Sockets命名空间还是在Win32中使用本地Winsock,你永远都不会看到IP/TCP/UDP头文件。这些头文件被剥离掉了,这样你在读取套接字时得到的是实际的有效负载,即发送的数据。

从我见过并使用套接字完成的所有事情的典型模式是,您可以在要发送的数据之前定义一个应用程序级标题。这个头文件至少应该包含要遵循的数据大小。这将允许您完整阅读每个“消息”,而不必猜测其大小。您可以随心所欲地使用它,例如,同步模式,CRC,版本,消息类型等,但“消息”的大小是您真正需要的

对于它的价值,我建议使用头部而不是包尾分隔符。我不确定EOP定界符是否存在重大缺陷,但头是我见过的大多数IP协议使用的方法。另外,从一开始就处理一条消息似乎更直观,而不是等待某种模式出现在我的数据流中,以表明我的消息已完成。

编辑:我只是刚刚意识到谷歌协议缓冲区项目。从我所知道的情况来看,这是WCF的二进制序列化/反序列化方案(我相信这太过简单化了)。如果您使用的是WCF,则不必担心发送的消息的大小,因为WCF管道在幕后处理此问题,这可能是为什么您没有在协议中找到与消息长度相关的任何内容缓冲区文件。然而,就插座而言,知道尺寸将如上所述有极大的帮助。我的猜测是,你将使用Protocol Buffers序列化你的数据,然后在你发送任何应用头之前发送它。在接收端,您将取消标题,然后对消息的其余部分进行反序列化。

+0

如果你的意思是protobuf-net,不,它不仅仅是WCF;项目中有套接字示例。 – 2009-03-26 08:31:52

3

我同意马特,一个头比一个协议缓冲区的页脚更好,因为PB是一个二进制协议,有一个问题是想出一个不是有效的消息序列的页脚。许多基于页脚的协议(通常是EOL)会工作,因为消息内容处于定义范围内(通常为0x20 - 0x7F ASCII)。

一个有用的方法是让你的最低级代码从套接字读取缓冲区,并将它们呈现给一个组装完整消息并记住部分消息的成帧层(我对此提出了一种异步方法(使用CCR) here,尽管用于线路协议)。

为了保持一致性,您可以始终将消息定义为具有三个字段的PB消息:固定长度作为长度,枚举作为类型,以及包含实际数据的字节序列。这使您的整个网络协议保持透明。

6

在派对上迟到的道歉。我是C#实现之一protobuf-net的作者。对于网络使用,你应该考虑“[De] SerializeWithLengthPrefix”方法 - 这样,它会自动处理你的长度。源代码中有例子。

我不会在旧帖子中详细讨论,但如果您想了解更多信息,请添加评论,我会尽快与您联系。