跳到主要内容

3. 流状态

3. 流状态 (Stream States)

本节从 stream 的发送组件和接收组件描述 stream. 这里描述两个状态机: 一个用于 endpoint 发送数据的 stream (第 3.1 节), 另一个用于 endpoint 接收数据的 stream (第 3.2 节).

unidirectional stream 根据 stream 类型和 endpoint 角色使用发送状态机或接收状态机. bidirectional stream 在两个 endpoint 上都使用这两个状态机. 在大多数情况下, 无论 stream 是 unidirectional 还是 bidirectional, 这些状态机的使用方式都是相同的. bidirectional stream 的打开条件稍微复杂一些, 因为发送端或接收端任一侧打开都会导致该 stream 在两个方向上打开.

本节展示的状态机主要是说明性的. 本文档使用 stream state 描述不同类型 frame 可以在何时以及如何发送的规则, 以及收到不同类型 frame 时预期的反应. 虽然这些状态机旨在有助于实现 QUIC, 但这些状态并不用于约束实现. 只要其行为与实现这些状态的实现保持一致, 实现可以定义不同的状态机.

备注

在某些情况下, 单个事件或动作可能导致经过多个状态的转换. 例如, 发送 FIN bit 被设置的 STREAM 可能使发送 stream 发生两次状态转换: 从 "Ready" 状态转换到 "Send" 状态, 再从 "Send" 状态转换到 "Data Sent" 状态.

3.1 发送流状态 (Sending Stream States)

图 2 展示 stream 中向 peer 发送数据的部分所处的状态.

       o
| Create Stream (Sending)
| Peer Creates Bidirectional Stream
v
+-------+
| Ready | Send RESET_STREAM
| |-----------------------.
+-------+ |
| |
| Send STREAM / |
| STREAM_DATA_BLOCKED |
v |
+-------+ |
| Send | Send RESET_STREAM |
| |---------------------->|
+-------+ |
| |
| Send STREAM + FIN |
v v
+-------+ +-------+
| Data | Send RESET_STREAM | Reset |
| Sent |------------------>| Sent |
+-------+ +-------+
| |
| Recv All ACKs | Recv ACK
v v
+-------+ +-------+
| Data | | Reset |
| Recvd | | Recvd |
+-------+ +-------+

图 2: Stream 发送部分的状态 (States for Sending Parts of Streams)

endpoint 发起的 stream (client 为类型 0 和 2, server 为类型 1 和 3) 的发送部分由应用打开. "Ready" 状态表示一个新创建的 stream, 它能够接受来自应用的数据. 在此状态下, stream 数据可能会被缓冲以准备发送.

发送第一个 STREAM 或 STREAM_DATA_BLOCKED frame 会使 stream 的发送部分进入 "Send" 状态. 实现可以选择推迟为 stream 分配 stream ID, 直到发送第一个 STREAM frame 并进入此状态, 这可以支持更好的 stream 优先级排序.

peer 发起的 bidirectional stream (server 为类型 0, client 为类型 1) 的发送部分在接收部分创建时从 "Ready" 状态开始.

在 "Send" 状态下, endpoint 在 STREAM frame 中传输 stream 数据, 并在必要时重传. endpoint 遵守 peer 设置的 flow control limit, 并继续接受和处理 MAX_STREAM_DATA frame. 如果 "Send" 状态下的 endpoint 因 stream flow control limit 而无法发送, 它会生成 STREAM_DATA_BLOCKED frame (第 4.1 节).

在应用指示所有 stream 数据都已发送, 并且包含 FIN bit 的 STREAM frame 已发送之后, stream 的发送部分进入 "Data Sent" 状态. 从此状态开始, endpoint 只在必要时重传 stream 数据. 对于处于此状态的 stream, endpoint 不需要检查 flow control limit, 也不需要发送 STREAM_DATA_BLOCKED frame. 在 peer 收到最终 stream offset 之前, 仍可能收到 MAX_STREAM_DATA frame. 对于处于此状态的 stream, endpoint 可以安全地忽略从 peer 收到的任何 MAX_STREAM_DATA frame.

一旦所有 stream 数据都被成功 acknowledged, stream 的发送部分进入 "Data Recvd" 状态, 这是一个 terminal state.

在 "Ready", "Send" 或 "Data Sent" 中的任一状态下, 应用都可以发出信号表示希望放弃传输 stream 数据. 或者, endpoint 可能从其 peer 收到 STOP_SENDING frame. 无论哪种情况, endpoint 都会发送 RESET_STREAM frame, 使 stream 进入 "Reset Sent" 状态.

endpoint MAY 将 RESET_STREAM 作为第一个提及某个 stream 的 frame 发送; 这会使该 stream 的发送部分打开, 然后立即转换到 "Reset Sent" 状态.

一旦包含 RESET_STREAM 的 packet 被 acknowledged, stream 的发送部分进入 "Reset Recvd" 状态, 这是一个 terminal state.

3.2 接收流状态 (Receiving Stream States)

图 3 展示 stream 中从 peer 接收数据的部分所处的状态. stream 接收部分的状态只映射 peer 上该 stream 发送部分的一部分状态. stream 的接收部分不会跟踪发送部分中无法观察到的状态, 例如 "Ready" 状态. 相反, stream 的接收部分会跟踪数据向应用的递送, 其中部分递送对发送方不可观察.

       o
| Recv STREAM / STREAM_DATA_BLOCKED / RESET_STREAM
| Create Bidirectional Stream (Sending)
| Recv MAX_STREAM_DATA / STOP_SENDING (Bidirectional)
| Create Higher-Numbered Stream
v
+-------+
| Recv | Recv RESET_STREAM
| |-----------------------.
+-------+ |
| |
| Recv STREAM + FIN |
v |
+-------+ |
| Size | Recv RESET_STREAM |
| Known |---------------------->|
+-------+ |
| |
| Recv All Data |
v v
+-------+ Recv RESET_STREAM +-------+
| Data |--- (optional) --->| Reset |
| Recvd | Recv All Data | Recvd |
+-------+<-- (optional) ----+-------+
| |
| App Read All Data | App Read Reset
v v
+-------+ +-------+
| Data | | Reset |
| Read | | Read |
+-------+ +-------+

图 3: Stream 接收部分的状态 (States for Receiving Parts of Streams)

peer 发起的 stream (client 为类型 1 和 3, server 为类型 0 和 2) 的接收部分, 在收到该 stream 的第一个 STREAM, STREAM_DATA_BLOCKED 或 RESET_STREAM frame 时创建. 对于 peer 发起的 bidirectional stream, 收到该 stream 发送部分的 MAX_STREAM_DATA 或 STOP_SENDING frame 也会创建接收部分. stream 接收部分的初始状态为 "Recv".

对于 bidirectional stream, 当 endpoint 发起的发送部分 (client 为类型 0, server 为类型 1) 进入 "Ready" 状态时, 接收部分进入 "Recv" 状态.

当 endpoint 从 peer 收到某个 stream 的 MAX_STREAM_DATA 或 STOP_SENDING frame 时, 它会打开一个 bidirectional stream. 对未打开的 stream 收到 MAX_STREAM_DATA frame 表示远端 peer 已打开该 stream 并正在提供 flow control credit. 对未打开的 stream 收到 STOP_SENDING frame 表示远端 peer 不再希望在该 stream 上接收数据. 如果 packet 丢失或重排序, 任一 frame 都可能先于 STREAM 或 STREAM_DATA_BLOCKED frame 到达.

在创建某个 stream 之前, 所有同类型且 stream ID 编号更低的 stream MUST 已被创建. 这确保两个 endpoint 上 stream 的创建顺序一致.

在 "Recv" 状态下, endpoint 接收 STREAM 和 STREAM_DATA_BLOCKED frame. 传入数据会被缓冲, 并可重新组装为正确顺序以递送给应用. 随着数据被应用消耗且缓冲空间可用, endpoint 会发送 MAX_STREAM_DATA frame, 允许 peer 发送更多数据.

当收到带有 FIN bit 的 STREAM frame 时, stream 的 final size 已知; 见第 4.5 节. stream 的接收部分随后进入 "Size Known" 状态. 在此状态下, endpoint 不再需要发送 MAX_STREAM_DATA frame; 它只接收 stream 数据的任何重传.

一旦该 stream 的所有数据都已收到, 接收部分进入 "Data Recvd" 状态. 这可能是由于收到同一个导致转换到 "Size Known" 的 STREAM frame. 在所有数据都已收到之后, 可以丢弃该 stream 的任何 STREAM 或 STREAM_DATA_BLOCKED frame.

"Data Recvd" 状态会一直保持到 stream 数据已递送给应用. 一旦 stream 数据已递送, stream 进入 "Data Read" 状态, 这是一个 terminal state.

在 "Recv" 或 "Size Known" 状态下收到 RESET_STREAM frame 会使 stream 进入 "Reset Recvd" 状态. 这可能导致向应用递送 stream 数据的过程被中断.

收到 RESET_STREAM 时, 所有 stream 数据可能已经收到 (即处于 "Data Recvd" 状态). 类似地, 在收到 RESET_STREAM frame 之后 (处于 "Reset Recvd" 状态), 剩余的 stream 数据也可能到达. 实现可以自行选择如何管理这种情况.

发送 RESET_STREAM 意味着 endpoint 无法保证 stream 数据的递送; 但是, 并没有要求在收到 RESET_STREAM 时不得递送 stream 数据. 实现 MAY 中断 stream 数据的递送, 丢弃任何尚未被消耗的数据, 并发出收到 RESET_STREAM 的信号. 如果 stream 数据已完整收到并已缓冲以供应用读取, RESET_STREAM 信号可以被抑制或暂不发出. 如果 RESET_STREAM 被抑制, stream 的接收部分仍保持在 "Data Recvd".

一旦应用收到表示 stream 已被 reset 的信号, stream 的接收部分转换到 "Reset Read" 状态, 这是一个 terminal state.

3.3 允许的 Frame 类型 (Permitted Frame Types)

stream 的发送方只发送三种会影响发送方或接收方处 stream 状态的 frame 类型: STREAM (第 19.8 节), STREAM_DATA_BLOCKED (第 19.13 节) 和 RESET_STREAM (第 19.4 节).

发送方 MUST NOT 从 terminal state ("Data Recvd" 或 "Reset Recvd") 发送这些 frame 中的任何一个. 对于处于 "Reset Sent" 状态或任何 terminal state 的 stream, 发送方 MUST NOT 发送 STREAM 或 STREAM_DATA_BLOCKED frame, 也就是说, 在发送 RESET_STREAM frame 之后不得发送. 由于承载这些 frame 的 packet 可能延迟递送, 接收方可能在任何状态下收到这三种 frame 中的任意一种. 在较晚状态收到 frame MUST NOT 被视为 error, 并且不会改变发送部分的状态.

接收方发送 MAX_STREAM_DATA (第 19.10 节) 和 STOP_SENDING frame (第 19.5 节).

接收方仅在 "Recv" 状态下发送 MAX_STREAM_DATA frame. 只要尚未收到 RESET_STREAM frame, 接收方可以在任何状态下发送 STOP_SENDING frame; 也就是除 "Reset Recvd" 或 "Reset Read" 之外的任何状态. 但是, 在 "Data Recvd" 状态下发送 STOP_SENDING frame 几乎没有价值, 因为所有 stream 数据都已收到. 由于 packet 延迟递送, 发送方可能在任何状态下收到这些 frame.

3.4 双向流状态 (Bidirectional Stream States)

bidirectional stream 由发送部分和接收部分组成. 实现可以将 bidirectional stream 的状态表示为发送部分和接收部分状态的组合. 最简单的模型是在发送部分或接收部分任一处于 non-terminal state 时将 stream 表示为 "open", 在两部分都处于 terminal state 时表示为 "closed".

表 2 展示一种更复杂的 bidirectional stream 状态映射, 它大致对应 HTTP/2 [HTTP2] 中的 stream 状态. 该表显示 stream 发送部分或接收部分的多个状态会映射到同一个组合状态. 注意, 这只是此类映射的一种可能; 此映射要求数据在转换到 "closed" 或 "half-closed" 状态之前已被 acknowledged.

发送部分 (Sending Part)接收部分 (Receiving Part)组合状态 (Composite State)
No Stream/ReadyNo Stream/Recv *1idle
Ready/Send/Data SentRecv/Size Knownopen
Ready/Send/Data SentData Recvd/Data Readhalf-closed (remote)
Ready/Send/Data SentReset Recvd/Reset Readhalf-closed (remote)
Data RecvdRecv/Size Knownhalf-closed (local)
Reset Sent/Reset RecvdRecv/Size Knownhalf-closed (local)
Reset Sent/Reset RecvdData Recvd/Data Readclosed
Reset Sent/Reset RecvdReset Recvd/Reset Readclosed
Data RecvdData Recvd/Data Readclosed
Data RecvdReset Recvd/Reset Readclosed

表 2: Stream 状态到 HTTP/2 的一种可能映射 (Possible Mapping of Stream States to HTTP/2)

*1

注意, "No Stream" 状态是假设性的; endpoint 及其 peer 都尚未创建该 stream.

3.5 请求的状态转换 (Solicited State Transitions)

如果应用不再关心它正在某个 stream 上接收的数据, 它可以中止读取该 stream 并指定 application error code.

如果 stream 处于 "Recv" 或 "Size Known" 状态, transport SHOULD 通过发送 STOP_SENDING frame 来发出该信号, 以促使相反方向的 stream 关闭. 这通常表示接收应用不再读取它从该 stream 接收的数据, 但并不保证传入数据会被忽略.

发送 STOP_SENDING frame 之后收到的 STREAM frame 仍计入 connection 和 stream flow control, 即使这些 frame 可以在收到时被丢弃.

STOP_SENDING frame 请求接收 endpoint 发送 RESET_STREAM frame. 收到 STOP_SENDING frame 的 endpoint, 如果该 stream 处于 "Ready" 或 "Send" 状态, MUST 发送 RESET_STREAM frame. 如果 stream 处于 "Data Sent" 状态, endpoint MAY 推迟发送 RESET_STREAM frame, 直到包含 outstanding data 的 packet 被 acknowledged 或被声明丢失. 如果任何 outstanding data 被声明丢失, endpoint SHOULD 发送 RESET_STREAM frame, 而不是重传该数据.

endpoint SHOULD 将 STOP_SENDING frame 中的 error code 复制到它发送的 RESET_STREAM frame 中, 但也可以使用任何 application error code. 发送 STOP_SENDING frame 的 endpoint MAY 忽略随后为该 stream 收到的任何 RESET_STREAM frame 中的 error code.

STOP_SENDING SHOULD 只针对尚未被 peer reset 的 stream 发送. STOP_SENDING 对处于 "Recv" 或 "Size Known" 状态的 stream 最有用.

如果包含先前 STOP_SENDING 的 packet 丢失, 预期 endpoint 会发送另一个 STOP_SENDING frame. 但是, 一旦该 stream 的所有 stream 数据或 RESET_STREAM frame 已经收到, 也就是说, 该 stream 处于除 "Recv" 或 "Size Known" 之外的任何状态, 就不需要发送 STOP_SENDING frame.

希望终止 bidirectional stream 两个方向的 endpoint 可以通过发送 RESET_STREAM frame 终止一个方向, 并可以通过发送 STOP_SENDING frame 促使相反方向尽快终止.