使用 SCTP 进行数据传输

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

概述

到目前为止,我已经回顾了 消息编码 关联初始化 。现在是时候看看 SCTP 是如何做一些实际工作的了——用户数据传输。它是通过 DATA 和 SACK 块实现的。只有当关联建立时,两个对等点才能交换用户数据,这意味着它应该处于 ESTABLISHED、SHUTDOWN-PENDING 或 SHUTDOWN_SENT 状态。 SCTP 接收方必须能够在单个数据包中接收至少 1500 个字节,这意味着它的初始 a_rwnd 值(在 INIT 或 INIT ACK 块中)不得设置为较低的值。与 TCP 类似,SCTP 支持在超过链路的 MTU 时对用户数据进行分段。分段数据的接收者将在将其传递给用户之前重新组合所有块。另一方面,可以将多个数据块捆绑在一条消息中。稍后将更详细地讨论数据碎片。

数据块

DATA 块在 3.3.1 节 中描述。这是块,它执行实际工作——将用户数据传输到对等点。带有 DATA 块的示例数据包可以在图 2 中看到。 1.它通常有以下标志:

  • E-bit - 结束片段位:如果设置,表示该块包含用户消息的最后一个片段。

  • B 位 - 开始片段位:如果设置,表示用户消息的第一个片段。

  • U 位 - 无序位:表示无序数据块,如果设置为 1。设置时,必须忽略流序列号参数。

  • I 位 - 立即位:在 RFC 7053 中描述。本文不予讨论。

数据块参数是:

  • TSN:数据块的传输序列号。如 :doc:`association initialisation <sctp-association-initialisation>` 中所述,它标识块用于确认和重传目的。每个 DATA 块都有唯一的 TSN,可用于稍后识别块,用于重传或重复数据检测等目的。来自同一发送者的数据块具有累积的 TSN 值。

  • 流标识符:标识有效负载所属的流。

  • Stream sequence number:表示payload在流中的序号。

  • 载荷协议标识符:标识载荷类型。该值是可选的,不被 SCTP 协议本身使用。

图 1: data.pcapng

SACK 块

SACK 块在 第 3.3.4 节 中定义。它的目的是确认一系列数据块。它还可以修改通告的接收器窗口 (a_rwnd) 或指示重复的数据块。 SACK 块的一个重要特征是指示传输中的间隙。这意味着一个或多个 DATA 块没有被对等方接收到,它们需要重新传输。差距将在下一节中讨论。在无花果。 2 您可以看到确认上一节中的数据的 SACK 块。它包含以下参数:

  • Cumulative TSN ACK:此参数表示在间隙之前(如果有)接收到的最新数据块的 TSN。

  • Advertised receiver windows credit (a_rwnd):a_rwnd 信用的新值。

  • Number of gap acknowledgement blocks:块中包含多少个间隙确认。在无花果。 2没有缝隙。

  • Number of duplicated TSNs:块中包含多少个重复的 TSN。图上没有2.

图 2: sack.pcapng

数据传输程序

在本节中,我们将回顾一些使用 SACK 块实现的数据传输相关过程。

如何报告差距

接收到的数据中可能存在间隙。这意味着接收方已收到 TSN 大于预期的数据块。在这种情况下,它将发送一个 SACK 块,其中“Cumulative TSN ACK”设置为间隙之前的 DATA 块的 TSN。然后“间隙确认块数”将设置为间隙数。所有间隙确认块将被插入到“重复的 TSN 数量”参数之后。每个块有两个整数:

  • Gap Ack Block Start:它包含此块中第一个 DATA 块的偏移量(请注意该值是偏移量,而不是 TSN)。这意味着当我们将“Cumulative TSN Ack”参数的值添加到此偏移量时,我们将获得间隔后第一个接收到的消息的 TSN。

  • Gap Ack Block End:它包含块中最后一个数据块的偏移量(再次 - 偏移量)。当我们将偏移量添加到“Cumulative TSN Ack”参数的值时,我们将获得最后接收到的数据块的 TSN。

让我们通过一个简单的例子看看它在现实世界中是如何工作的。主机 A 和 B 之间建立了关联,A 向 B 发送八个数据块。 A 的初始 TSN 值为 100。因此数据块的 TSN 为 100 到 107。假设块 100、101、104、105 和 107 已成功交付,并且由于某种原因,块 102、103 和 106 已成功交付B没有收到。这意味着传输中有两个间隙。整个数据流如图所示。 3 并且标记了间隙。此时 B 决定用 SACK chunk 回复。让我们看看应该在块参数中设置什么:

  • Cumulative TSN Ack:如您所知,此参数包含间隙之前最后接收到的数据块的 TSN。在我们的例子中,这是第二条消息的 TSN - 101。

  • Advertised receiver windows credit (a_rwnd):我们现在对此参数不感兴趣,所以我们将跳过它。

  • Number of gap acknowledgement blocks:我们有两个gap,所以这个参数的值为2。

  • 第一个间隙确认块:

    • Gap Ack Block Start:Gap之后第一个接收到的DATA chunk是TSN 104。累计TSN Ack为101。所以这个参数的值为104 - 101 = 3。

    • Gap Ack Block End:该块包含两个具有累积 TSN 的数据块。最后一个块的 TSN 是 105。这意味着这个参数的值是 105 - 101 = 4。

  • 第二个间隙确认块:

    • Gap Ack Block Start:同样的逻辑在这里起作用。在前一个块之后有一个丢失的数据块。接收到的chunk的TSN为107,参数值为107 - 101 = 6。

    • Gap Ack Block End:这个block只包含一个chunk,所以参数的值又是6。start和end的值都是6,说明这个block只包含一个chunk。

图 3:数据传输中的差距

如何报告重复的 TSN

您也可能因为某些原因(例如丢失的 SACK)接收到重复的 DATA 块。 SCTP 使用 SACK 块向发送方报告重复的数据。您还记得每个 SACK 都有“重复的 TSN 数量”参数,它指定该块包含多少个重复的 TSN 块。这些块总是在 Gap Ack 块之后添加。 Duplicated TSN 块只有一个值 - 重复的 TSN 本身。

SCTP 堆栈在内部保留重复 TSN 的计数。准备 SACK 块时,所有重复项都会添加到消息中,并且会重置内部计数。 Duplicated TSN 块没有显示计数的字段,因此如果一个块被接收两次以上,则在 Duplicated TSN 块中多次出现 TSN。

让我们看看这在实践中是如何工作的。图 4 显示了两个节点 - A 和 B 之间的示例流。我们假设关联已经建立并且该图仅包含 DATA 和 SACK 块。

图 4:重复的数据块

A 将 TSN 为 100 的数据块发送给 B 两次。在那之后 B 决定发送 SACK 并且它在消息中包含重复的 TSN - 它被接收了两次,这使得一个重复。然后 A 再发送一次 TSN 100 的 DATA 和另一个 TSN 101 的 DATA 三次。 B再次发送SACK。因为已经收到 100,所以它包含在重复的 TSN 列表中。由于重复 TSN 的计数已重置,在最后一个 SACK 之后,TSN 100 仅在重复 TSN 块中设置一次。之后 B 收到了 3 次 TSN 101 的数据块。这产生了两个副本,因此 101 被添加到 Duplicated TSNs 块中两次。

有序和无序数据交付

通常 SCTP 在特定流中“按顺序”传送用户数据,这意味着接收方以与发送消息相同的顺序获取消息。例如,如果 A 发送消息 1、2 和 3,B 将以完全相同的顺序接收它们 - 1、2 和 3,而不是 2、1、3。所有有序的数据块都有他的 U 位(在块标志中) 设置为 0,表示有序传递,并且 'Stream sequence number' 参数会为每条新消息递增 1。您可以在图上看到 DATA 块。 1 - U 位设置为零且“流序列号”也为零,这意味着这是来自该流的发送方的第一条消息。下一个块的序号将为 1,依此类推。如果 SCTP 堆栈已将流序列号为零的用户数据块交付给用户,然后接收到序列号为 2 的新数据块,则堆栈应该保留它直到接收到序列号为 1 的数据块并将它们按顺序交付给用户.

SCTP 还支持无序数据传递。在这种情况下,U 位设置为 1,流序列号参数被忽略。接收方应该尽快将每个无序块交付给用户,而不是出于任何原因保留它。

用户数据的分片与重组

如果用户消息大小超过关联的 MTU 大小,SCTP 应该对消息进行分段或返回错误(如果它不支持分段)。有效载荷被分割,使得每个分段消息的大小加上 SCTP 协议开销小于关联的 MTU。每个块都有单独的顺序 TSN。在第一个 DATA 的 chunk flags 中,B-flag 设置为 1,E-flag 设置为 0。对于最后一个 chunk,B-flag 设置为 0,E-flag 设置为 1。第一个和最后一个之间的所有块都将两个位都设置为 0。接收方使用相同的逻辑来检测分段消息的第一个和最后一个块,然后将其重新组合并将其交付给用户。

概括

在这篇文章中,我们回顾了 SCTP 中的用户数据传输过程,包括一些错误指示,如传输间隙和重复消息。我希望 up-know 您已经很好地了解 SCTP 关联是如何建立的以及用户消息是如何传输的。当然,如果您觉得需要有关任何主题的更多信息,请自行阅读规范。这是让自己对协议感到满意的最佳方式。

在下面的帖子中,我们将回顾 SCTP 心跳关联如何拆除工作。谢谢阅读。