跳到主要内容

3. Compression method (压缩方法)

3. Compression method (压缩方法)​

头信息的大部分在分组流的生命周期内保持不变.对于非 TCP 分组流, 几乎所有头字段都是常量.对于 TCP, 许多字段是常量, 而其他字段以小的和可预测的值改变.

为了开始对分组流的头进行压缩, 通过链路传输携带上下文标识符 CID 的完整头.压缩器和解压缩器将该完整头的大部分字段存储为上下文.上下文由头的字段组成, 这些字段的值是常量, 因此根本不需要通过链路发送, 或者在连续头之间变化很小, 以致于发送与先前值的差异所使用的比特比发送绝对值少.

在预期在分组流中为常量的字段中的任何更改都将导致压缩器再次发送完整头以更新解压缩器处的上下文.只要压缩器和解压缩器的上下文相同, 头就可以被解压缩为与压缩前完全相同.但是, 如果在传输期间丢失了完整头或压缩头, 解压缩器的上下文可能会过时, 因为它没有正确更新.然后, 压缩头将被不正确地解压缩.

IPv6 不打算用于可以将大量损坏分组传递给 IPv6 模块的链路.这意味着链路必须具有非常低的位错误率, 或者链路层帧必须受到强校验和、前向纠错或类似性质的保护.头压缩不应该用于没有强链路层校验和的 IPv4.损坏的帧将因此被链路层丢弃.链路层实现可能向头压缩模块指示帧已损坏, 但它无法说明它属于哪个分组流, 因为可能是 CID 损坏了.此外, 帧可能在链路层实现不知道的情况下消失, 例如如果链路是多跳链路, 其中帧可能由于每跳的拥塞而被丢弃.因此, 头压缩模块应该处理和防止的链路错误类型将是分组丢失.

因此, 头压缩方案需要机制来更新解压缩器处的上下文并检测或避免不正确的解压缩.这些机制对于 TCP 和非 TCP 流非常不同, 并在第 3.2 节和第 3.3 节中描述.

本文档中的压缩机制假设分组在压缩器和解压缩器之间不会重新排序.如果链路会重新排序, 第 11 节描述了在解压缩之前对分组进行排序的机制.还假设链路层实现可以提供分组的长度, 并且 UDP 分组或隧道分组中没有填充.

3.1 Packet types (分组类型)​

除了 IPv4 和 IPv6 分组类型之外, 此压缩方法使用四种分组类型.链路层分组类型和分组的前四位的值的组合唯一确定分组类型.有关如何表示这些分组类型的详细信息在第 13 节中.

FULL_HEADER - 表示具有未压缩头的分组, 包括 CID, 以及如果不是 TCP 分组, 则包括代.它为 CID 标识的分组流建立或刷新上下文.

COMPRESSED_NON_TCP - 表示具有压缩头的非 TCP 分组.压缩头由标识用于解压缩的上下文的 CID、用于检测不一致上下文的代以及头的随机改变字段组成.

COMPRESSED_TCP - 表示具有压缩 TCP 头的分组, 包含 CID、标识已更改字段的标志八位字节以及编码为与先前值的差异的已更改字段.

COMPRESSED_TCP_NODELTA - 表示具有压缩 TCP 头的分组, 其中通常作为与先前值的差异发送的所有字段都按原样发送.此分组类型仅作为对来自解压缩器的头请求的响应而发送.它不能作为重传的结果而发送.

除了用于压缩的分组类型之外, 只要压缩器决定不压缩分组, 就使用常规 IPv4 和 IPv6 分组.可以使用额外的分组类型来加速通过链路的 TCP 流的修复, 其中解压缩器可以向压缩器发送分组.

CONTEXT_STATE - 表示从解压缩器发送到压缩器的特殊分组, 以传达已丢失同步的 (TCP) CID 列表.此分组仅通过单个链路发送, 因此不需要 IP 头.格式在第 10.2 节中显示.

3.2 Lost packets in TCP packet streams (TCP 分组流中的丢包)​

由于 TCP 头使用与先前 TCP 头的差异进行压缩, 因此丢失具有压缩或完整头的分组将导致后续压缩头被不正确地解压缩, 因为用于解压缩的上下文没有正确递增.

丢失压缩的 TCP 头将导致随后解压缩的 TCP 头的 TCP 序列号偏离 k, 其中 k 是丢失段的大小.这种不正确解压缩的 TCP 头将被 TCP 接收器丢弃, 因为 TCP 校验和可靠地捕获了合理 k 的序列号中的 "偏离 k" 错误.

TCP 的修复机制最终将重传丢弃的段, 并且压缩器窥视 TCP 头以检测 TCP 何时重传.当这发生时, 压缩器发送完整头, 假设重传是由于解压缩器处的不匹配压缩状态.[RFC-1144] 对此机制有很好的解释.

应使用第 10 节的机制来加速上下文的修复.这对于具有高分组丢失率的中速链路很重要, 例如无线.在链路上每次丢失分组后, 由于上下文不一致而丢失超时值的分组是不可接受的, 尤其是当 TCP 连接是通过广域网时.

3.3 Lost packets in UDP and other non-TCP packet streams (UDP 及其他非 TCP 分组流中的丢包)​

不正确解压缩的 UDP 分组头和其他非 TCP 分组头不像 TCP 分组那样受到校验和的良好保护.没有序列号会变成 "偏离 k" 并且几乎保证校验和失败, 就像 TCP 那样.UDP 校验和仅覆盖有效载荷、UDP 头和伪头.伪头包括源和目的地地址、传输协议类型和传输分组的长度.除了那些字段之外, IPv6 头的大部分不被 UDP 校验和覆盖.此外, 其他非 TCP 头完全缺乏校验和, 例如分片.

为了安全地避免非 TCP 头的不正确解压缩, 非 TCP 分组流的上下文的每个版本都由一个代标识, 这是一个由建立和刷新上下文的完整头携带的小数字.压缩头携带用于压缩它们的上下文的代值.当解压缩器看到压缩头携带的代值不是其该分组流上下文的代时, 上下文不是最新的, 并且必须丢弃或存储分组, 直到完整头建立正确的上下文.

差分编码不用于非 TCP 流, 因此压缩的非 TCP 头不会改变上下文.因此, 丢失压缩头不会使具有压缩头的后续分组无效.此外, 代仅在完整头的上下文与先前完整头的上下文不同时才改变.这意味着丢失完整头仅在完整头实际上会改变上下文时才会使解压缩器的上下文过时.

代字段长度为 6 位, 因此代值在上下文的 64 次更改后重复自身.为了避免在错误突发或其他临时中断后的不正确解压缩, 压缩器不能在短于 MIN_WRAP 秒的时间后重复使用相同的代值.已断开连接 MIN_WRAP 秒或更长时间的解压缩器必须等待下一个完整头才能解压缩.压缩器必须在启动后至少等待 MIN_WRAP 秒才能压缩非 TCP 头.压缩器可以切换到另一个 CID 或发送常规头, 直到 MIN_WRAP 秒过去, 而不是过早重复使用代值.MIN_WRAP 的值在第 14 节中找到.

3.3.1 Compression Slow-Start (压缩慢启动)​

为了允许解压缩器从丢失会改变上下文的完整头中快速恢复, 在上下文更改后以指数增长的周期定期发送完整头.此技术避免了其他压缩方案使用的压缩器和解压缩器之间的消息交换, 例如在 [RFC-1553] 中.这种交换对于无线移动设备来说可能代价高昂, 因为发射器会消耗更多功率, 并且在发送和接收之间切换可能会引入延迟.此外, 需要消息交换的技术不能用于单工链路, 例如直接广播卫星通道或有线电视系统, 并且很难适应多址链路上的多播.

|.|..|....|........|................|..............................
^
Change Sent packets: | with full header, . with compressed header

图中显示了更改后如何发送分组.压缩器为每个非 TCP 分组流保留一个变量 F_PERIOD, 该变量跟踪在完整头之间可以发送多少个压缩头.当非 TCP 分组流的头改变以致其上下文改变时, 发送完整头并将 F_PERIOD 设置为 1.在发送 F_PERIOD 个压缩头之后, 发送完整头.在压缩慢启动期间每次发送完整头时, F_PERIOD 加倍.

3.3.2 Periodic Header Refreshes (周期性头刷新)​

为了避免在接收器丢失其上下文时丢失太多分组, 在可以在头刷新之间发送的具有压缩头的非 TCP 分组数量上存在上限 F_MAX_PERIOD.如果要发送分组, 并且自上次为该分组流发送完整头以来已发送 F_MAX_PERIOD 个压缩头, 则必须发送完整头.

为了避免低数据速率分组流的长时间断开连接, 非 TCP 分组流中完整头之间的时间也有上限 F_MAX_TIME.如果要发送分组, 并且自上次为该分组流发送完整头以来已经过了超过 F_MAX_TIME 秒, 则必须发送完整头.F_MAX_PERIOD 和 F_MAX_TIME 的值在第 14 节中找到.

3.3.3 Rules for sending Full Headers (发送完整头的规则)​

压缩器可以使用以下伪代码来确定何时为非 TCP 分组流发送完整头.代码维护两个变量:

C_NUM       -- 自上次发送完整头以来发送的压缩头的计数.
F_LAST -- 发送最后一个完整头的时间.

并使用函数

current_time()       返回当前时间
min(a,b) 返回 a 和 b 中较小的值

过程 send_full_header(), increment_generation_value() 和 send_compressed_header() 执行显而易见的操作.

if ( <this header changes the context> )

C_NUM := 0;
F_LAST := current_time();
F_PERIOD := 1;
increment_generation_value();
send_full_header();

elseif ( C_NUM >= F_PERIOD )

C_NUM := 0;
F_LAST := current_time();
F_PERIOD := min(2 * F_PERIOD, F_MAX_PERIOD);
send_full_header();

elseif ( current_time() > F_LAST + F_MAX_TIME )

C_NUM := 0;
F_LAST := current_time();
send_full_header();

else

C_NUM := C_NUM + 1
send_compressed_header();

endif

3.3.4 Cost of sending Header Refreshes (发送头刷新的成本)​

如果每第 f 个分组携带完整头, H 是完整头的大小, C 是压缩头的大小, 则平均头大小为

(H-C)/f + C

对于 f > 1, 平均头大小比压缩头大 (H-C)/f.

在为各种 f 值绘制平均头大小的图表中, 曲线中有一个明显的拐点, 即, 存在一个极限, 超过该极限进一步增加 f 会产生递减的回报.F_MAX_PERIOD 应该被选择为曲线拐点右侧的频率.对于典型的 H 和 C 大小, 例如完整头 (IPv6/UDP) 为 48 个八位字节, 压缩头为 4 个八位字节, 设置 F_MAX_PERIOD > 44 意味着完整头将对平均头大小贡献少于一个八位字节.使用四地址路由头, F_MAX_PERIOD > 115 将具有相同的效果.

默认的 F_MAX_PERIOD 值 256 (第 14 节) 将完整头频率放置在拐点右侧, 并意味着完整头通常将对平均头大小贡献显著少于一个八位字节.对于 H = 48 和 C = 4, 在达到由默认 F_MAX_PERIOD 确定的稳态头刷新频率后, 完整头对平均头大小贡献约 1.4 位.1.4 位是非常小的开销.

在上下文更改后, 指数退避方案最初会频繁发送完整头.在发送 9 个完整头和 255 个压缩头之后将达到默认的 F_MAX_PERIOD.对于每个分组具有 20 ms 语音样本的典型语音流, 这相当于略超过 5 秒.

在整个退避期间, 当 H = 48 和 C = 4 时, 完整头对平均头大小贡献 1.5 个八位字节.对于 20 ms 语音样本, 完整头对平均头大小贡献少于一个八位字节需要不到 1.3 秒, 并且在这初始 1.3 秒期间, 完整头对平均头大小添加少于 4 个八位字节.指数退避的成本不高, 并且由于非 TCP 分组流的头预期很少改变, 它将在很长时间内摊销.

头刷新在带宽方面的成本高于类似的硬状态方案 (如 [RFC-1553]) 的成本, 在该方案中, 完整头必须由解压缩器确认才能发送压缩头.此类方案通常在上下文改变时发送一个完整头加上几条控制消息.硬状态方案需要更多类型的协议消息, 并且需要消息交换.硬状态方案还需要显式处理软状态自动处理的各种错误条件, 例如一方意外消失的情况, 这是无线链路上的常见情况, 其中移动设备可能超出基站的范围.

软状态方案的主要优点是不需要压缩器和解压缩器之间的握手, 因此该方案可以用于单工链路.在带宽方面的成本高于硬状态方案, 但解压缩器的简单性、协议的简单性以及压缩器和解压缩器之间缺少握手证明了这种小成本是合理的.此外, 软状态方案更容易扩展到多址链路上的多播, 例如无线链路.