如何检测何时完全接收到协议缓冲区消息?

这是我的另一个问题的一个分支。 如果你愿意,请阅读它,但没有必要。

基本上,我意识到为了在大型消息上有效地使用C#的BeginReceive(),我需要(a)首先读取数据包长度,然后准确读取多个字节或(b)使用数据包结束分隔符。 我的问题是,这些都存在于协议缓冲区吗? 我还没有使用过它们,但是在文档中看起来似乎没有长度标题或分隔符。

如果没有,我该怎么办? 我应该构建消息然后使用长度标头/ EOP分隔符前缀/后缀吗?

您需要在协议中包含大小或结束标记。 除了支持任意分解成单独数据包的不确定八位字节流(并且数据包也可以在传输中溢出)之外,基于流的套接字(TCP / IP)不构建任何内容。

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

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

知道有效负载大小允许您继续读取足以用于剩余消息的多个字节(如果读取完成的次数较少,则对剩余字节执行另一次读取,直到收到整个消息为止)。

使用结束消息指示符也可以,但您需要定义如何处理包含相同八位字节序列的消息…

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

我不会详细介绍旧post,但如果您想了解更多信息,请添加评论,我会尽快回复您。

我同意Matt的说法,标题比协议缓冲区的页脚好,主要原因是,由于PB是二进制协议,所以提出一个也不是有效消息序列的页脚是有问题的。 许多基于页脚的协议(通常是EOL协议)都可以工作,因为消息内容在一个定义的范围内(通常为0x20 – 0x7F ASCII)。

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

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

TCP / IP以及UDP包都包含对其大小的一些引用。 IP标头包含一个16位字段,用于指定IP标头的长度以字节为单位的数据。 TCP标头包含一个4位字段,用32位字指定TCP标头的大小。 UDP标头包含一个16位字段,用于指定UDP标头的长度以字节为单位的数据。

这就是事情。

使用Windows中的标准普通套接字,无论您是在C#中使用System.Net.Sockets命名空间还是在Win32中使用本机Winsock,您都不会看到IP / TCP / UDP标头。 这些标题被剥离,以便您在读取套接字时获得的是实际有效负载,即发送的数据。

我使用套接字看到和完成的所有内容的典型模式是您定义一个应用程序级标头,该标头位于您要发送的数据之前。 此标头至少应包含要遵循的数据大小。 这将允许您完整地阅读每个“消息”,而无需猜测其大小。 您可以随心所欲地使用它,例如,同步模式,CRC,版本,消息类型等,但“消息”的大小是您真正需要的。

对于它的价值,我建议使用标头而不是数据包结束分隔符。 我不确定EOP分隔符是否存在明显的缺点,但标头是我见过的大多数IP协议使用的方法。 另外,我似乎更直观地从头开始处理消息,而不是等待某些模式出现在我的流中以指示我的消息已完成。

编辑:我刚才意识到Google Protocol Buffers项目。 据我所知,它是WCF的二进制序列化/反序列化方案(我确信这是一个粗略的过度简化)。 如果您正在使用WCF,则不必担心正在发送的消息的大小,因为WCF管道在幕后处理这个问题,这可能是您在协议中找不到与消息长度相关的任何内容的原因。缓冲文档。 但是,在sockets的情况下,如上所述,知道尺寸将有很大帮助。 我的猜测是,您将使用Protocol Buffers序列化您的数据,然后在发送之前确定您提出的任何应用程序头。 在接收端,您将拉出标头,然后反序列化消息的其余部分。