网络开发人员对 SCTP 的介绍

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

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

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

SCTP 是一种传输层协议,其功能类似于 TCP,并增加了一些独特的功能。尽管它在互联网世界中不是很流行,但该协议在电信网络中发挥着重要作用。它用作各种 SIGTRAN 协议的传输协议,并且是 EPC 中的默认传输协议。

有一篇 维基百科 文章提供了 SCTP 功能的基本概述。另一个很好的文档是 SCTP:它是什么,如何使用它? 由 Randal Stewart、Michael Tuxen 和 Peter Lei 撰写。在本文中,我想从网络开发人员的角度提供 SCTP 概述,这(在我看来)意味着深入研究一些规范。您将在这里找到我认为最有趣的部分的快速演练,并参考了 SCTP 规范 RFC 4960 中的相应部分。

一些 SCTP 相关的关键术语和缩写

1.3 1.4 节描述了一些 SCTP 关键术语和缩写。对于初学者来说,最重要的是关联、流和块。

协会

与 TCP 一样,SCTP 也是面向连接的协议。两个对等点之间的逻辑连接称为“关联”。与 TCP 不同的是,SCTP 关联可以在双方的多个 IP 地址之间建立。一个例子如图所示。 1. 主机A有3个以太网口,分别有3个不同的IP地址。主机 B 也有三个接口,但只有两个用于关联。接口是关联的一部分,以浅蓝色显示。每个节点的地址在关联建立期间公布,这将是另一篇文章的主题。一个重要的注意事项是 SCTP 端口号对于对等体上的所有 IP 地址都是相同的。此功能称为多宿主。

溪流

每个关联都有逻辑单向通道。确切的计数是在关联建立期间商定的,并且每个方向都可以不同。例如在图。 1,从主机 A 到主机 B 有 7 个流,但从主机 B 到主机 A - 只有 3 个。流的目的是为数据/消息的有序传输提供逻辑通道。这意味着如果流 X 中的消息丢失并需要重传,它不会延迟流 Y 中的消息,因为它们在逻辑上是独立的。然而,流 X 中的所有其他消息(在丢失的消息之后)将在重传后发送并被延迟。这种效应称为队头阻塞,是 TCP 协议的主要问题之一。

大块

大块是最简单的。这是 SCTP 数据包中的一个信息单元。

图一:SCTP关联图

1.5 节 提供了协议的功能概述。它描述了如何使用流、如何在流中实现有序传递、如何确认数据消息等。

SCTP数据包结构

现在让我们看一下 SCTP 数据包的结构。

公用头

一个 SCTP 数据包包含公共标头和一个或多个块。每个 SCTP 数据包的公共标头具有相同的结构。它包含了:

  • 源端口号(16 位)

  • 目的端口号(16 位)

  • 验证标签(32 位)

  • 校验和(32 位)

图上的数据包样本。 2 是公共标头,从 INIT 消息中剥离(为了更好的可读性,INIT 块已折叠)。更多详细信息,请参见 第 3.1 节

图 2: init.pcapng

大块

一个SCTP块代表一个协议消息,它可以被协议本身使用(例如INIT,这是关联建立的第一步),或者可以包含用户数据(DATA块)。它具有三个固定长度的参数和一个可变长度的值。每个块都是 4 字节对齐的。这意味着整个块大小必须是 4 字节的倍数。如果不是,则添加零字节填充以实现 4 字节的倍数要求。填充不应超过 3 个字节。 4 字节填充没有意义,因为消息已经是 4 字节对齐的。每个块都有 git 以下字段:

  • 块类型 - 8 位,可以被认为是 SCTP 消息的标识符。例如 INIT、DATA、HEARTBEAT 等。

  • 块标志 - 8 位,特定于每个块类型。

  • 块长度 - 16 位,块的长度(以字节为单位),包括所有标头字段(类型、标志、长度)加上值的长度,不包括填充。

  • 块值 - 可变长度参数。包含各种块参数,这取决于块类型。

图 3 显示了 SACK 消息,它包含公共数据包头和 SACK 块。块的大小为 16 字节,这意味着没有填充。块结构在 第 3.2 节 中描述。所有块定义都可以在 第 3.3 节 中找到。

图 3: sack.pcapng

块参数

每个块的值也有特定的格式。它包含两个固定大小的参数和可变长度的值。块中的每个参数必须是 4 字节对齐的,因此块参数本身也可能包含填充。逻辑与块值相同。下面是每个参数的结构:

  • chunk 参数类型 - 16 位,chunk 中参数的类型。

  • 块参数长度 - 16 位,参数的长度,不带填充。

  • 块参数值 - 指定参数类型的实际值。

一个例子

我使用 wireshark,它使数据包解码非常容易,因此我们将手动“解码”一个块,看看它是如何完成的。来自图的数据包。 3 包含一个 SACK 块并有 4 个参数。让我们检查第一个 - 累积 TSN ACK:

  1. 用 Wireshark 打开 sack.pcapng 。我们对以太网或 IP 数据不感兴趣——我们将只检查 SCTP。单击框架(只有一个)并从协议列表中选择 Stream Control Transmission Protocol。 SCTP 数据标记在窗口底部的 HEX 编辑器中。
  2. 公共标头具有固定长度的参数。我们有 0x115c 和 0xc668 作为源和目标端口号(4444 和 50792)。然后有 32 位用于验证标记 - 0x88e5e376 和校验和 0x883432df。这就是通用标头的全部内容。由于固定长度的参数,这里没什么特别的。

  3. 随后是一个 SCTP 块。它的标题是固定长度的,所以让我们阅读它。 8 位块类型 (0x03)、8 位标志 (0x00) 和 16 位长度 (0x0010)。现在我们可以获得有效载荷。 0x0010 为 16,表示该 chunk 包含 16 个字节(不是位,长度以字节为单位)。让我们将其转换为位(16*8 = 128)并减去到现在为止读取的参数的长度。 128 - 8 (type) - 8 (flags) - 16 (length) = 96。这意味着后面的 96 位是块数据。再次检查 HEX 视图中的数据包,您会看到我们还剩下 96 位。这意味着块参数值为 8a 70 48 4a 00 01 9f fa 00 00 00 00。

  4. 现在我们知道了 chnk 类型——0x03。快速查看 第 3.2 节 显示该块是选择性确认 (SACK)。我们需要找到块定义 - 它在 第 3.3.4 节 中。它以消息的描述开始。现在我们对此不感兴趣,我们只想解析参数及其值。

  5. 第 3.3.4 节 的描述之后,有整个消息的“位图”和每个参数的详细信息。我们开始一一解析。

  6. 块类型、标志和长度已经被解析。我们知道标志值是 0x00 并且描述说 SACK 的标志总是 0。这里没有惊喜。

  7. 下一个是 Cumulative TSN Ack:32 位。这意味着它的值为 0x8a70484a,即 2322614346。未解析的有效负载为 00 01 9f fa 00 00 00 00。

  8. 列表中的下一个参数是 Advertised Receiver Window Credit (a_rwnd):32 位。它的值为0x00019ffa,也就是106490。payload未解析的部分现在是00 00 00 00。

  9. 下一个 - 间隙确认块数:16 位(无符号整数)。它的值为 0x0000,这意味着该块中没有“Gap Ack Blocks”参数。好消息 - 减少我们的工作量。现在我们只需要解析 00 00。

  10. 下一个参数是 Number of Duplicate TSNs:16 位。它的值为 0x0000。这意味着没有“重复的 TSN”参数。

  11. 根据最后两个解析的参数,没有'Gap Ack Block Start'和'Gap Ack Block End',所以我们完成了。

同样的方法可以应用于解码 3.2 节 中的任何参数。只需按照字段列表并注意可选参数即可。

下一步是什么?

那么到目前为止我们讨论了什么?我们知道为什么要设计 SCTP 以及它的用途,并且我们已经介绍了一些关键术语。我们还了解了 SCTP 数据包的结构以及如何解码公共标头和块。好的开始,但有趣的部分即将到来。请继续关注下一部分,它将讨论协议过程和其中涉及的消息。