在链路层层传输的帧分为两种类型:数据帧和应答帧。
数据帧是发送数据的承载体,应答帧为接收者收到数据帧的应答。如果发送者在指定时间内没有收到接收者的应答,将会对数据进行重传。帧格式如下所示:
[HEAD][ID][ATTR]<DATA>[CHK][TAIL]
帧的各个域表示的含义如下(除DATA域外,其他每个域都为1个字节):
HEAD | 帧头,标识一帧的开始。 |
ID | 帧ID,相邻的两帧数据帧帧ID不能相同。应答帧帧ID与对应的数据帧ID一致。帧ID没重新发送一次数据帧自增1。取值范围时0-255,超过255时重新从0开始。 |
ATTR | 帧的相关属性,如帧类型,是否重发帧等。每个bit表示的含义如下: Bit0:0-数据帧 1-应答帧 |
DATA | 数据域,变长。 |
CHK | [ID][ATTR]<DATA>的CRC8校验。 |
TAIL | 帧尾,标识一帧的结束。 |
注1:特定两个节点通讯时,传输层保证一次只能一帧数据进行传输和应答。
注2:帧中不包含长度信息,因为我们的常规应用数据域的一般比较短,大部分是1~5个字节,并且有CRC校验,不必要有长度信息。
转义字符
对于除帧头、帧尾的传输数据外,其他字符如果是如帧头、帧尾和转义字符等特殊数据,为了提高解包的准确性,需要进行转义处理。转义规则如下:
名称 | 值 | 转义 |
HEAD | 0x55 | ESC+0xE7 |
TAIL | 0xAA | ESC+0xE8 |
ESC | 0x1B | ESC+0x00 |
转义字符将除帧头帧尾外的其他数据中可能的帧头帧尾进行转义,减少了尝试解包的次数,提升协议解包效率。
注:HEAD和TAIL不使用STX和ETX的原因是STX和ETX的ASCII分别未0x02和0x03,数据出现0x02和0x03的几率比较大,如果使用STX和ETX作为帧头帧尾,对于我们的应用,会加大转义的概率。
超时重发
发送者发送数据,如果在规定时间内收到正确应答,说明接收者正确接收到数据,本次传输完成,如果没有收到应答,可能的情况有:
- 数据未正确到达接收者;
- 接收者正确数据,但是发送者未正确收到应答帧。
如果发送者在规定时间内没有收到应答帧,直接重新尝试向接收者发送数据帧。
对于接收者,如果收到数据帧,首先检测数据是否已经收到过,如果已收到过,直接应答,如果没有收到过,则接收数据并应答。
如何判断是否已接收过数据帧?
记录最近3次收到的帧ID,收到数据帧判断帧ID是否为接收过的即可。
数据可靠性
为保证数据的可靠性,协议通过以下几个方面来保证。
数据封包:将数据封装成帧格式,从一定层面上可以保证数据的可靠性,如果帧被破坏,数据解包将发生错误,杜绝收到错误数据。
超时重发:如果发送者在指定的时间内没有收到接收者的应答,说明数据可能未能到达目的地,发送者将进行数据重发,在一定程度上提高数据的抗干扰能力。
CRC校验:CRC校验保证数据的正确性,同时是校验的折衷选择。和校验抗干扰能力太弱,其他校验太过复杂影响协议栈性能。
特殊字符转义:对于传输的除帧头、帧尾的数据外,其他数据如果遇到特殊字符(帧头、帧尾和转义字符本身),需要对数据进行转义,提高帧数据解包的准确性。