流


🌐 Streams

流是 QUIC 中主要的数据承载抽象。一旦会话建立,流可以由本地端点或远程端点发起。

🌐 Streams are the primary data-carrying abstraction in QUIC. A stream can be initiated by either the local endpoint or the remote peer once a session is established.

数据流可以是双向的(数据在两个方向流动)或单向的(数据只在一个方向流动)。quic 模块提供了用于创建每种类型的独立 API:session.createBidirectionalStream() 和 session.createUnidirectionalStream()。由远程对端发起的数据流会通过 session.onstream 回调传递。当协商的应用协议支持流级回调(例如 HTTP/3)并且配置了 onheaders 回调时,传入的数据流可以完全通过它进行消费,注册 onstream 则是可选的。

🌐 Streams can be either bidirectional (data flows in both directions) or unidirectional (data flows in only one direction). The quic module provides separate APIs for creating each kind: session.createBidirectionalStream() and session.createUnidirectionalStream(). Streams initiated by a remote peer are delivered via the session.onstream callback. When the negotiated application protocol supports the stream-level callbacks (e.g. HTTP/3) and an onheaders callback is configured, incoming streams can instead be consumed entirely through it and registering onstream is optional.

有两种方式可以将数据写入流:

🌐 There are two ways to write data to a stream:

  • 主体来源 — 在创建流时传入 body 选项(或调用 stream.setBody())。主体可以是字符串、ArrayBuffer、ArrayBufferView、Blob、FileHandle、AsyncIterable、同步的 Iterable,或解析为这些任意类型的 Promise。一个 null 主体会立即关闭可写端。当数据已经可用或可以表示为可迭代对象时,这是最简单的方法。
  • Writer — 使用 stream.writer 以增量方式推送数据。该 writer 提供同步方法(writeSync()、writevSync()、endSync()),会立即返回,也有异步对应方法(write()、writev()、end())。异步的 write() 和 writev() 方法遵循流/迭代严格的背压策略:当写入缓冲区已满时,它们会返回 ERR_INVALID_STATE 拒绝,而不会等待容量。如果有排队的 drain,end() 会在关闭前先等待它。写入前检查 writer.canWrite。要等待容量,可以使用来自 node:stream/iter 的 ondrain(),然后重试写入。流的 onblocked 回调会报告传输流控阻塞了进度,但不会表示 writer 容量再次可用。writeSync() 在写入缓冲区满时返回 false;调用方应使用 ondrain() 等待再重试。

对于给定的流,这两种方法是互斥的。

🌐 These two approaches are mutually exclusive for a given stream.

读取是通过将流作为异步可迭代对象进行迭代来完成的。每次迭代都会产生一批 Uint8Array 块:

🌐 Reading is done by iterating the stream as an async iterable. Each iteration yields a batch of Uint8Array chunks:

for await (const chunks of stream) {
  for (const chunk of chunks) {
    // Process each Uint8Array chunk
  }
} 

每个流只能获取一个异步迭代器。该流也兼容 node:stream/iter 工具,例如 Stream.bytes()、Stream.text() 和 Stream.pipeTo()。

🌐 Only one async iterator can be obtained per stream. The stream is also compatible with node:stream/iter utilities such as Stream.bytes(), Stream.text(), and Stream.pipeTo().