透彻解析QUIC协议(第二部分):详解QUIC中的数据流机制
本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一、QUIC流的特性
Streams in QUIC provide a lightweight, ordered byte-stream abstraction to an application.
QUIC中的流为应用程序提供了一个轻量级的,有序的字节流抽象。
QUIC流有以下几种特性:
- 有序
应用层在使用QUIC流时,不需要考虑数据的乱序问题,QUIC在协议层为我们提供了这种保障。
- 支持单向、双向
QUIC支持创建单向流或双向流,单向流只允许发送端发送数据,双向流允许双端发送数据。
- 流创建不限制数量
在同一个连接上可以创建任意数量的流,满足多路复用的需求。
- 在流上发送数据无限制
在每路流上发送的数据量是没有限制的,可大可小,例如你可以使用一路流仅仅每分钟发送一次心跳,而在另一路流上发送每秒几Mb的视频数据。
二、QUIC流的细节
stream ID与流类型的关系
流ID的形式是62位整形数字,生成流ID时要保障该流ID在连接内是唯一的,并且打开一个新流时是不能复用已关闭的流ID的。
流ID的最低两位与流的类型有关:
最低位标识了流的发起方,值为0表示该流由客户端发起,值为1表示该流由服务端发起。
倒数第二位标识流的方向,值为0表示这是一条双向流,值为1表示该流是单向流。
最低有效位 | 第二低有效位 | 值 | 发起方 | 流方向 |
---|---|---|---|---|
0 | 0 | 0x0 | 客户端 | 双向 |
1 | 0 | 0x1 | 服务端 | 双向 |
0 | 1 | 0x2 | 客户端 | 单向 |
1 | 1 | 0x3 | 服务端 | 单向 |
数据的收发
应用数据被封装在流桢内发送,在同一路流内使用offset确定数据的顺序。接收端的实现会缓存乱序的数据以在将数据排序后交给上层。
发送端在同一路流上不允许使用相同的offset发送不同的数据,如果发送端这样做了,接收端会抛出一个PROTOCOL_VIOLATION的连接异常。
发送端在未确认发送的数据是否在对端流控允许范围内时,也不允许发送数据。
优先级
可以在应用层指定流的相对优先级。
在某些应用场景下区分流的优先级是很有用的:
例如两路流分别传输音频、视频数据,人对声音卡顿的敏感度是要高于视频的,这种情况下一般都会优先保障音频的传输,在网络状况不好的情况下允许一定程度的视频卡顿。此时将音频数据流的优先级设置的更高,就能较容易的实现这点。
流支持的操作
对QUIC流可以进行以下几类操作:
发送端操作
-
发送端可以依照流控的指示向流写数据。
-
可以重置流。
-
可以终止流。
关于重置流和终止流的具体含义和细节将在后面讨论。
接收端操作
-
读数据。
-
可以终止读数据(同时通知发送端)。
注册回调
在实际使用QUIC协议栈时,应用程序很多情况下会关心流的状态,QUIC提供了以下几类可注册的回调:
-
流打开、关闭的状态转换。
-
对方中止了读。
-
有数据可读。
-
流可写、流不可写的状态转换。
三、本篇总结
-
QUIC流在协议层保障了数据有序,应用层在使用QUIC流时无需关注乱序问题。
-
流有单双向之分,使用时可以按需创建(使用单向流在某些实现上或许能够节约一些资源)。
-
创建流的数量、在各路流上发送的数据量都是无限制的,能很好的满足多路复用需求。
-
Stream ID的最低两个有效位标识了流的发起方与方向。
-
可以根据应用场景的需要设置流的优先级,在网络条件差时,优先级高的流会被更好的传输。
-
发送端、接收端对流可以进行不同的操作(当然一个终端可以即是发送端又是接收端),使用QUIC api时可以注册一些回调关注流的状态,例如流的打开、关闭、可读、可写、被中止等。
上一篇: 蚂蚁金服应用了 QUIC 协议
下一篇: 简单易懂!详解QUIC协议的工作原理