VJ压缩算法总结
算法介绍
VJ压缩算法是一种适用于串行链路的TCP/IP头压缩算法,对小包有较高的压缩率,可以有效提高串行链路的链路利用率,并有效提高交互式应用的响应速度
算法概述
在一个TCP/IP连接活动期间,在连接上发送的TCP/IP包的包头有大部分信息是保持不变的,这部分信息可以被压缩掉。如果发送方把这部分被压缩掉的信息用一个连接ID来替代,接收方保存上一次收到的TCP/IP包头,则接收方就可以根据存储的信息恢复被压缩掉的数据,链路上的TCP/IP头压缩就得以实现。
算法原理
1.压缩
发送方通过串行链路驱动来发送IP数据报,IP数据报经过压缩器,压缩器检查这个IP数据报是否封装了一个TCP报文段,如果不是就把它标记为TYPE_IP,并封装后发送;否则就在本地存储的一个包头数组中搜索该TCP报文段对应的连接,如果找到,就压缩这个IP数据报,把包头拷贝到查找到的槽位,标记这个包为COMPRESSED_TCP并成帧后发送;如果未找到对应的连接,就把数组中最旧的条目删除,并复制TCP/IP头到这个槽位,同时标记这个包为UNCOMPRESSED_TCP,并发送到对端。UNCOMPRESSED_TCP标记的包没有被压缩,它和正常的IP数据报的唯一区别是其“协议”字段被连接ID替代。连接ID是该TCP/IP数据报头在包头数组中的位置索引。这也是发送法用来同步接受方的方法。接受方收到这个报文后把它按照连接ID存放在数组中,用以解压后续的压缩数据包。
2.解压缩
解压缩器根据收到的不同类型的包做不同的转换:
1) 对TYPE_IP类型的包,简单地把它提交给上层协议(IP层) 2) 对UNCOMPRESSED_TCP类型的包,从IP头的协议类型字段提取出连接ID,并把该字段恢复成IPPROTO_TCP,连接ID被用于在本地数组中存储恢复后的TCP/IP头,连接ID即是该报文头在数组中的索引。
3) 对COMPRESSED_TCP类型的包,用包头的连接ID定位到保存的包头,根据存储的包头恢复压缩的数据包头,并用恢复后的包头更新存储的包头。压缩/解压缩的流程如图1所示
图1 SLIP VJ压缩/解压缩算法实现
3.压缩包的格式
被压缩过的TCP/IP报文头部如图2所示:
图2 压缩后的TCP/IP报头
Byte 0 是一个报头变化掩码,用以指示这个报头相对与上一个报头变化的字段,Byte 1是连接标识号,接收方可用它来定位这个连接上一个被存储的报头。Byte 2 – 3是TCP的校验和,接下来就是变化掩码所指定的变化的字段。Delta标示发送的是差值。 由于一个连接可能持续较长时间,而在这段时间内,连接标识符是不变的,也可以被压缩掉,C标志位被清除的时候表示连接标识符不变。
算法参考实现
1.压缩器
压缩器对输入的IP包做如下检验:
1) 如果四层协议不是TCP,把它标记为TYPE_IP并发送 2) 如果分组是IP分片,不做压缩,标记为TYPE_IP并发送
3) 如果TCP的SYN, FIN, 或是RST标志被设置或是ACK标志没有设置,则认为该TCP段不可压缩,标记为TYPE_IP并发送
4) 通过前3个检查的包将以UNCOMPRESSED_TCP或COMPRESSED_TCP的形式发送
5) 如果没有找到对应该报文的连接,则老化一个连接(使用LRU算法),并存储该报文头,把改报文的协议类型字段替换为连接号,把它作为UNCOMPRESSED_TCP类型的报文发送
6) 如果找到了对应该报文的连接,则把存储的包头和当前包头做比较,如果protocol version, header length, type of service, don’t fragment, time-to-live, data offset, IP options和TCP options字段有变化,则发送一个UNCOMPRESSED_TCP类型的报文 7) 如果第6步中列举的字段都没有变化,则压缩该TCP/IP报头。
a) 如果URG标志被设置,则把紧急指针字段拷入压缩后的报文头,并设置变化掩码的U位。如果URG标志未设置,则比较当前报文头和保存报文头的紧急指针字段,如果有变化,则不压缩当前报文,把它作为UNCOMPRESSED_TCP处理。
b) 计算当前报文头和存储报文头窗口字段的差值,如果不为0,在压缩报文中存储该差值,并设置change mask的W位
c) 计算TCP确认序列号字段的差值,如果结果小于0或是大于2-1,则不压缩报文,把它作为UNCOMPRESSED_TCP处理,否则,如果结构非0,在压缩报头中记录该差值并设置change mask的A位
d) 计算TCP序号之间的差值,如果结果小于0或是大于2-1,则不压缩报文,把它作为UNCOMPRESSED_TCP处理,否则,如果结构非0,在压缩报头中记录该差值并设置change mask的S位
e) U, A, S, W位计算完之后开始检查特殊情况:
如果U, S和W位都被设置,则不执行压缩,发送一个UNCOMPRESSED_TCP的包。
如果只有S位被设置,检查序号的改变值是否等于上一个分组中的用户数据长度,如果相等,修改change mask 为SAWU,并丢弃压缩报文中的序号变化值 如果只有S和A被设置,检查它们的变化量是否相等,并等于上一个分组的用户数据长度,如果相等,设这change mask为SWU,并丢弃已计算出的序号和确认序号改变值。
如果报头没有任何改变,查看该分组是否有用户数据(在这种情况下,这个TCP段可能是一个重复的ACK或是窗口探针)或者前一个分组包含用户数据(在这种
1616情况下这个分组是一个重传的分组),如果这两种情况中的任何一种出现,发送一个UNCOMPRESSED_TCP类型的分组。
f) 计算packet ID字段的差值,如果不是1,设置change mask的I位,并保存差值 g) 如果PUSH标志被设置,则设置change mask的P位 h) 把当前的TCP/IP报头拷贝到报头数组中 i) 在原始报文中用压缩后的报头替换原始报头 j) 压缩后的报文传给下层传输
2.解缩压器
解压缩器工作在接收端,它根据压缩器的指示处理压缩报头。解压缩器可能收到4种报文:压缩器产生的3种报文以及由接收器在检测到帧错误时产生的TYPE_ERROR伪报文。解压缩器根据报文的不同类型做不同的处理:
如果是TYPE_ERROR或是未知类型的报文,设置状态中的一个to标志以丢弃所有COMPRESSED_TCP类型的报文,直到设置C标志的COMPRESSED_TCP类型的报文到达或是UNCOMPRESSED_TCP类型的报文到达。此时不返回任何报文
如果是TYPE_IP类型的报文,不做任何处理,直接返回。
如果是UNCOMPRESSED_TCP类型的报文,检查IP PROTOCOL的连接号字段,如果非法,则设置to标志,并停止处理;否则清除to标志,拷贝TCP/IP头到该字段指定的位置,并记录这个连接,恢复IP PROTOCOL字段为TCP(6)并返回这个报文。
如果是COMPRESSED_TCP类型的报文,做如下处理:
如果change mask 的C位被设置,则检查压缩报头中的connection number字段,如果非法,设置to标志并停止处理,否则记录这个连接,并清除to标志 如果change mask 的C标志没有设置,并且to被置位,丢弃该分组。 把压缩报头的TCP校验和拷贝到存储报头中。
如果change mask的P标志被置位,则设置存储报头的PUSH标志
如果change mask的S, A, W, U位都被设置,计算上一个分组用户数据的长度(用存储报头的total length字段减去TCP和IP头的长度),并加上存储报头的TCP序号字段作为新的TCP序号。
如果S, W, U位被设置,而A没有设置,则计算上一个TCP段中用户数据的长度,并把它分别加上存储报头的TCP序号和ACK序号作为新的序号。 否则按照压缩器设置的顺序来解释change mask的各个字段
上述过程结束后,重新计算IP的total length(收到的用户数据长度加上存储的TCP/IP报头长度)和IP头校验和,至此完全解压了压缩的TCP/IP头。