5.3 接收分片(Fragment)

接收端收到某个 Packet 的第一个分片后,首先创建 Packet 对象,用发送端传过来的 packId 作为序号,状态为 FRAGMENTING,记录当前时间为 Packet 开始接收时间,并把 Packet 放入接收缓存中。解析 data 帧,读取分片序号 fragId,并用 fragId 作为序号创建状态为 ACK 的 Fragment 对象,根据 FIN 标识判断是否是最后一个分片,并对 Fragment 对象进行标识。解析出的数据写入 Fragment。最后把 Fragment 放入 Packet 的分片集合中。然后发送 ACK 帧给数据发送端。

通过 Session 发送 ACK 帧格式

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-------------------------------+-------------------------------+
|        [id from client]       |        [id from server]       |
+-------------------------------+-------------------------------+
|                        timestamp ...                          |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                    ... timestamp [curent time]                |
+-+-+-+-----+-+-+-------+-------+-------------------------------+
|S|P|F| RSV |S|F|  RSV  |OPCODE |          Payload len          |
|T|A|R| [0] |L|I|  [0]  |[0x4]  |        [Payload.length]       |
|R|C|A|     |O|N|       |       |                               |
|E|K|G|     |W| |       |       |                               |
|0|1|1|     |0|0|       |       |                               |
+-+-+-+-----+-+-+-------+-------+-------------------------------+
|                      Packet Id [packId]                       |
+---------------------------------------------------------------+
|                     Fragment Id [fragId]                      |
+---------------------------------------------------------------+
:                      Payload [ack bits]                       :
+---------------------------------------------------------------+

timestamp 字段写入当前时间戳,精确到毫秒。STRE 字段设置为 0,PACK、FRAG 字段设置为 1,SLOW 设置为 0,如果最后一个分片 FIN 设置为 1,否则设置为 0。RSV 字段设置为 0。OPCODE 字段设置为 0x4,Payload len 字段设置为 Payload 实际长度。Packet Id 字段写入数据包序号(packId)。Fragment Id 字段写入接收端收到的最大的分片序号。Payload 写入 ack bits。ack bits 是一个 bit 列表,每个 bit 代表相应的 Fragment 是否已接收完成。每个字节可以标识 8 个分片。从最大的分片号开始,依次构造 ack bits,并写入 Payload 字段。

通过 Stream 发送 ACK 帧格式

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-------------------------------+-------------------------------+
|        [id from client]       |        [id from server]       |
+-------------------------------+-------------------------------+
|                        timestamp ...                          |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                    ... timestamp [curent time]                |
+-+-+-+-----+-+-+-------+-------+-------------------------------+
|S|P|F| RSV |S|F|  RSV  |OPCODE |          Payload len          |
|T|A|R| [0] |L|I|  [0]  |[0x4]  |        [Payload.length]       |
|R|C|A|     |O|N|       |       |                               |
|E|K|G|     |W| |       |       |                               |
|1|1|1|     |0|0|       |       |                               |
+-+-+-+-----+-+-+-------+-------+-------------------------------+
|              Stream Id [random id from client]                |
+---------------------------------------------------------------+
|                      Packet Id [packId]                       |
+---------------------------------------------------------------+
|                     Fragment Id [fragId]                      |
+---------------------------------------------------------------+
:                      Payload [ack bits]                       :
+---------------------------------------------------------------+

STRE 字段设置为 1,Stream Id 字段写入当前流 ID,其他字段设置和上面一致。

发送端收到 ACK 帧后,从发送缓存中取出 Packet 对象,解析出 Payload 中的 ack bits,每个 bit 标识相应的 Fragment 是否已接收完成,找出已接收完成的分片序号,从 Packet 对象中取出 Fragment 对象,更新状态为 ACK,更新分片发送完成时间为当前时间。