我刚刚问了一个关于如何发送大于 SendBufferSize 的数据的问题,答案是分几部分发送。
我的第二个问题是如何接收这些数据?它会在网络流中完成还是将其分开。
我刚刚问了一个关于如何发送大于 SendBufferSize 的数据的问题,答案是分几部分发送。
我的第二个问题是如何接收这些数据?它会在网络流中完成还是将其分开。
TCP 不是仅消息协议。它是一个基于流的协议,负责为我们拆分数据。让我带你到核心部分。在 TCP 的情况下有两个部分 - 分段和分段。
分段发生在 TCP(传输层)。这取决于两个参数 - MSS 和窗口大小。MSS 确定设备可以接收的最大分段大小。MSS 在初始连接建立期间通过 TCP 选项进行通信。每个数据流方向可以使用不同的 MSS,由操作系统决定。但是,窗口大小由接收方在 TCP 标头中发送,以在等待来自接收主机的确认和窗口更新之前,在连接的接收方一次可以缓冲的最大数据量进行通信。也就是说,主机可以在用尽接收器的窗口大小之前发送多个段(MSS 因子)。
分段在 IP(网络层)以两种方式发生。如果在发送方和接收方之间的通信路径上没有 MTU 限制的设备,则仅满足以太网的 MTU(1500 字节)就会发生分片。但是,如果在发送方和接收方之间存在具有 MTU 限制的中间设备,IP 层(互联网协议)会进行数据报分段,以便可以形成数据包,以便它们可以通过具有较小最大传输单元的链路( MTU) 大于原始数据报的大小。对于具有 MTU 限制的中间设备,发送方还应部署路径 MTU 发现以确定网络路径中朝向接收方的最小 MTU,并动态调整 MSS 以避免网络内的 IP 分片。路径 MTU 发现是通过设置 DF (Don' t Fragment) 传出数据包的 IP 标头中的选项。发送方和接收方之间的通信路径中的任何设备,其 MTU 小于数据包将丢弃此类数据包,并使用包含设备 MTU 的 ICMP“Destination Unreachable (Datagram Too Big)”消息回复发送方。此信息允许发送方适当地减少其假定路径 MTU。
因此,这导致了 MSS 和 MTU 之间的关系点。RFC 791 声明“所有主机都必须准备好接受最多 576 个八位字节的数据报(无论它们是完整还是分段到达)”。因此,IP 网络的最小 MTU 为 576。在 TCP 的情况下,减去 TCP 标头的 20 个字节和 IP 标头的 20 个字节,我们将给我们 536 个字节作为 TCP 的标准 MSS。
现在,让我们进入重新组装部分。可以根据中间设备上的 MTU 进行分段,但重新组装只能在目标设备上进行。TCP 负责分段,但是在目标设备上,TCP 负责排序,但是段的重新组装应该由应用程序负责。
因此,如果您只需要基于整个消息的通信并且不关心可靠性,那么 UDP 将是您的选择。但是请注意,如果您可以通过拆分发送大数据,UDP将无法确保数据包的顺序,也无法处理丢包,因为它没有像重传这样的纠错机制.
如果您希望像在 UDP 中一样进行基于消息的通信,但要结合 TCP 功能,例如可靠的按序交付、拥塞控制以及在其之上的其他改进/功能,例如多流、多宿主、in-内置 MTU 发现,那么 SCTP 应该是您的选择。但是,如果您的网络中有旧的 NAT 系统,那么您可能需要将 SCTP 封装在 UDP 中。
.Net 为您处理数据拆分。您可以在不注意的情况下编写和接收大于 SendBufferSize 的包(如果您不太关心性能)。
与例如UDP 相比,TCP 不是一种允许发送单个“消息”的协议,这些“消息”以相同的形式传递给接收者。您宁愿发送和接收字节流。
在幕后,您的数据被拆分为 IP 数据包,而不管,SendBufferSize
顺便说一下,默认值为 8192,该值远高于适合单个 IP 数据包的值(IP 数据包的最大大小与MTU相关,但主要是小于 1500 字节)。例子:
另外:多个Socket.Send()
s 的数据可能会被合并(Nagle 算法)。例子:
并且接收者无法区分这两种情况。更重要的是,出于效率的原因,在接收端,数据可能会在不同大小的缓冲区中交给您。
底线:使用 TCP,您不能依赖接收与发送时相同数量和相同大小的数据包。如果您需要,请按照 Stephen Cleary 的建议添加大小信息,或者使用 UDP 之类的协议,或者如果您需要可靠性:SCTP。然而,这看起来有点矫枉过正,除非您也对 SCTP 对 TCP 的其他改进感兴趣。
您将需要在您的协议中称为消息帧的东西。
有趣的事实:即使发送小于SendBufferSize
. :)
您的数据可能会在接收时被拆分,因此您需要在收到所有数据后重建它。