7

想象一下,你和我正在通过 TCP 发送一个很长的句子(比如 1024000 字节)。

如果您给我写一个 1024000 字节的句子,您实际上是使用 NetworkStream 将这些字节写入其中。

当我收到时,我应该提前知道您发送的句子的大小吗?

如果没有,我该如何检查何时应该停止 stream.read?

如果是,程序是否应该具有将数据大小嵌入数据头部的功能?所以我先收到 4 个字节,看看我应该读多少个字节?

.Net 是否有任何东西可以自动在传输中嵌入数据大小?

4

9 回答 9

4

.NET 和 TCP 协议都没有内置任何东西来预先定义消息的大小。TCP 协议仅指定所有数据将被传输到接收端点(或至少将尽最大努力这样做)。

您全权负责定义一种方法,让接收者知道要读取多少数据。正如其他人所指出的那样,您如何执行此操作的细节取决于您要传输的内容的性质:您可以像您提到的那样首先发送长度,您可以编码称为终止符的特殊序列,您可以使用预定义的数据块所以所有消息都具有相同的大小,等等。

编辑

这开始是一个评论,但它不仅仅是符合这个限制。

NULL 添加到流中仅意味着附加一个二进制值为 0 的字符(不要与字符混淆0)。根据您用于传输的编码(即 ASCII、UTF-8、UTF-16 等),可能会转化为发送一个或多个 0 字节,但如果您使用适当的翻译,您只需输入类似\0in你的字符串。这是一个例子:

string textToSend = "This is a NULL Terminated text\0";
byte[] bufferToSend = Encoding.UTF8Encoding.GetBytes(textToSend);

当然,以上所有假设都假设您发送的所有其余数据不包含任何其他 NULL。这意味着它是文本,而不是任意二进制数据(例如文件的内容)。这很重要!否则你不能使用 NULL 作为消息终止符,你必须想出另一个方案。

于 2010-02-16T14:49:59.093 回答
2

一般来说,使用具有数据大小的标题比使用终止字符更好。终止字符方法容易受到拒绝服务攻击。我可以继续向您的服务发送数据,只要我不包含终止符,您就需要继续处理(并可能分配内存)直到崩溃。

使用包含总大小的标头,如果传输太大而您无法处理,您可以忽略它,或者发回错误。如果恶意方尝试发送的数据多于标头中声明的数据,您会在下一个流的开头注意到一个损坏的标头并忽略它。

于 2010-02-16T14:58:28.467 回答
1

当我收到时,我应该事先知道您发送的句子的大小吗?

这可能会有所帮助(对于渲染进度条之类的事情),但不一定是必需的。

如果没有,我该如何检查何时应该停止 stream.read?

您的流的内容定义了这一点。例如,许多消息对一些信息进行编码,告诉您该消息已结束(例如,一个空字节表示字符串的结尾,或</html>表示 HTML 文档的结尾)。

于 2010-02-16T14:45:54.593 回答
1

有两种方法可以做到这一点,一种是您描述的方式 - 将消息的大小放在标题中 - 另一种是在流上放置某种终止标记。例如,如果您的消息保证没有嵌入NUL字符,您可以以NUL.

于 2010-02-16T14:47:55.553 回答
1

如果您知道或可以轻松找出消息的总长度,我建议您提前发送。如果确定它不可能或非常昂贵,您可以使用类似于HTTP中的分块传输编码的东西。

于 2010-02-16T14:53:45.517 回答
1

要点是,对于 TCP,传输端的套接字写入的数量和大小与接收端的套接字读取的数量/大小之间没有对应关系。

如果数据流具有某种结构,则必须在有效负载周围添加某种元/包装数据。

每当我不得不解决这个问题时,我都会使用以下组合:

a)使用幻数来指示数据消息的开始或结束(或两者)

b)在消息末尾使用校验和来验证内容是否正确(我知道 TCP 执行错误检查和重传,但校验和在接收器偶然出现的开始/结束幻数的情况下很有用/流中的序列)

c) 在初始幻数之后使用长度字段(假设发送方在传输开始之前知道数据的长度)

在开始之前,请仔细查看为您使用的语言/平台实现了哪些更高级别的协议库。网络流?是 Windows API/MFC 什么的。

例如,我最近不得不设置一个客户端/服务器系统。客户端和服务器功能已经用 python 编写,因此只需使用 python xmlrpclib/server 就可以很容易地将这两个程序连接在一起 - 从字面上复制示例,我在 30 分钟内就完成了。如果我自己直接在 tcp 上编写了一些自制协议,那将是 5 天!

于 2010-02-16T15:00:27.870 回答
0

由于 TCP 是一种可靠的协议,您可以构建您的协议以指示即将到来的字节数,或者使用某种终止符来指示传输结束。如果您使用的 UDP 不能保证是可靠的,那么构建一个能够承受丢弃字节的协议或指示自包含终止的数据包以来预期有多少字节(并具有重传机制)将更为重要可能会丢失。最大数据传输时间和超时也可能有用,但前提是您可以确定一个合理的最大值。

于 2010-02-16T14:48:44.513 回答
0

我的回答是否定的。特别是对于大型数据集。原因是首先发送大小会增加系统的延迟

如果要先发送大小,则需要在开始发送之前计算整个答案。

另一方面,如果您使用终止标记,您可以在数据的第一个比特准备好后立即开始发送它们,同时计算以下数据。

于 2010-02-16T15:01:29.037 回答
0

You may also want to investigate the BinaryReader/BinaryWriter classes which can be wrapped around any stream, TCP or otherwise.

These support, among other functions, reading/writing strings (in an encoding of your choice) while taking care of including the length of the string too.

于 2010-02-16T17:51:29.700 回答