背景

在流式协议一章中,我们使用了固定长度头部来处理消息边界问题,但是固定长度头部按预定义的固定顺序有和长度排列,每个字段的位置和大小在协议设计时已经确定,无法动态调节。

示例:IP头部固定为20个字节,其中包含版本、TTL、源地址等字段,每个字段的位置和长度固定。

那么,如果我们考虑后期协议的拓展,固定字段就很难做出调整,这样我们就必须设计多种协议,发布多个软件版本,不容易维护。由此,大佬们就提出来了TLV(Type-Length-Value)。

TLV

1.基本结构

TLV每个字段按类型(Type)、长度(Length)、值(Value)三元组组织:

(1)Type字段

作用:唯一标识数据类型,决定如何解析Value.

编码规则

定长:通常1-4字节(如0x01表示整数,0x02表示字符串)。

(2)Length字段

作用:明确Value的字节长度,避免解析时依赖分隔符。

**编码方式: **

1.定长模式:固定长度(如1字节表示Value最大长度为255字节,4字节则表示最大长度为4GB)。

2.变长模式(BER编码):

若长度<=127,用1字节表示。

若长度>127,则首字节最高位必须为1,低7位表示后续有多少字节来表示Value的长度。

示例:BER编码下Length字段为0x82 0x01 0x2C,其中首字节0x82为1000 0010,其高位为1,剩余7位数值为2,表示首字节后续2位为Value的长度,即0x01 0x2C为300,表示Value的实际长度为300字节。

(3)Value字段

作用:存储实际数据,格式由Type定义。

常见类型:

基础类型:整数(大小端)、浮点数、字符串。

复合类型:数组、结构体,甚至是嵌套TLV。

示例

假设我们定义了一种TLV结构,其结构体定义如下:

struct TLV {

uint8_t type; // Type: 假设有三种类型分别为:整数(0x01),浮点数(0x02),字符串(0x03)

uint16_t length; // Length: 定长模式,固定2个字节大小来表示Value的长度,即最大长度为65535字节

std::string value; // Value: 实际数据内容

};

这里,我们给出以下TCP消息,然后以我们定义的TLV结构去解读一下。

[Type:0x03(1B) Length:0x0005(2B) Value:0x48656C6C6F(5B)]

1.Type:0x03(1B) ->表示类型为0x03,即为字符串类型,且该字段大小为1个字节(1B);

2.Length:0x0005(2B) ->表示长度为0x0005,即Value的长度为5个字节,该字段大小为2个字节(2B);

3.Value:0x48656C6C6F(5B) ->表示实际内容为0x48656C6C6F,因为类型是字符串,所以我们解析出来是"Hello"。