跳到主要内容

3. 示例 (Examples)

本节给出若干简短示例, 展示分块 GET 以及 PUT 或 POST 的消息流. 这些示例演示基本操作、有重传时的操作, 以及块大小协商的操作示例.

在所有这些示例中, Block 选项以分解形式表示: 先给出 Block 选项类型 (1 或 2), 后跟冒号, 然后是用斜杠分隔的块编号 (NUM)、more bit (M) 和块大小指数 (2**(SZX+4)). 例如, Block2 Option 值 33 会表示为 2:2/0/32, Block1 Option 值 59 会表示为 1:3/1/128.

与 [RFC7252] 中一样, "MID" 用作 "Message ID" 的缩写.

3.1. Block2 示例 (Block2 Examples)

第一个示例 (Figure 2) 展示了被拆分为三个块的 GET 请求. 服务器提出块大小为 128, 客户端同意. 前两个 ACK 各包含 128 字节负载, 第三个 ACK 包含 1 到 128 字节之间的负载.

CLIENT                                                     SERVER
| |
| CON [MID=1234], GET, /status ------> |
| |
| <------ ACK [MID=1234], 2.05 Content, 2:0/1/128 |
| |
| CON [MID=1235], GET, /status, 2:1/0/128 ------> |
| |
| <------ ACK [MID=1235], 2.05 Content, 2:1/1/128 |
| |
| CON [MID=1236], GET, /status, 2:2/0/128 ------> |
| |
| <------ ACK [MID=1236], 2.05 Content, 2:2/0/128 |

Figure 2: 简单分块 GET

在第二个示例 (Figure 3) 中, 客户端预期会发生分块传输 (例如, 因为 link-format 描述 [RFC6690] 中有大小指示), 并发送块大小建议. 除最后一个 ACK 外, 所有 ACK 消息都携带 64 字节负载; 最后一个 ACK 携带 1 到 64 字节之间的负载.

CLIENT                                                     SERVER
| |
| CON [MID=1234], GET, /status, 2:0/0/64 ------> |
| |
| <------ ACK [MID=1234], 2.05 Content, 2:0/1/64 |
| |
| CON [MID=1235], GET, /status, 2:1/0/64 ------> |
| |
| <------ ACK [MID=1235], 2.05 Content, 2:1/1/64 |
: :
: ... :
: :
| CON [MID=1238], GET, /status, 2:4/0/64 ------> |
| |
| <------ ACK [MID=1238], 2.05 Content, 2:4/1/64 |
| |
| CON [MID=1239], GET, /status, 2:5/0/64 ------> |
| |
| <------ ACK [MID=1239], 2.05 Content, 2:5/0/64 |

Figure 3: 带早期协商的分块 GET

在第三个示例 (Figure 4) 中, 客户端未预料到需要分块传输, 且不满意服务器单方面选择的大小. 由于客户端最初没有发送大小建议, 协商只会影响从第二次消息交换开始的大小. 由于客户端已在第一次 128 字节交换中获得了第一个和第二个 64 字节块, 它继续请求第三个 64 字节块 ("2/0/64"). 服务器不理解这些细节 (也不需要理解), 只是尽力响应请求.

CLIENT                                                     SERVER
| |
| CON [MID=1234], GET, /status ------> |
| |
| <------ ACK [MID=1234], 2.05 Content, 2:0/1/128 |
| |
| CON [MID=1235], GET, /status, 2:2/0/64 ------> |
| |
| <------ ACK [MID=1235], 2.05 Content, 2:2/1/64 |
| |
| CON [MID=1236], GET, /status, 2:3/0/64 ------> |
| |
| <------ ACK [MID=1236], 2.05 Content, 2:3/1/64 |
| |
| CON [MID=1237], GET, /status, 2:4/0/64 ------> |
| |
| <------ ACK [MID=1237], 2.05 Content, 2:4/1/64 |
| |
| CON [MID=1238], GET, /status, 2:5/0/64 ------> |
| |
| <------ ACK [MID=1238], 2.05 Content, 2:5/0/64 |

Figure 4: 带后期协商的分块 GET

在所有这些情况 (以及后续情况) 中, 重传由 CoAP 消息交换层处理, 因此不会影响块操作 (Figure 5 和 Figure 6).

CLIENT                                                     SERVER
| |
| CON [MID=1234], GET, /status ------> |
| |
| <------ ACK [MID=1234], 2.05 Content, 2:0/1/128 |
| |
| CON [MID=1235], GE///////////////////////// |
| |
| (timeout) |
| |
| CON [MID=1235], GET, /status, 2:2/0/64 ------> |
| |
| <------ ACK [MID=1235], 2.05 Content, 2:2/1/64 |
: :
: ... :
: :
| CON [MID=1238], GET, /status, 2:5/0/64 ------> |
| |
| <------ ACK [MID=1238], 2.05 Content, 2:5/0/64 |

Figure 5: 带后期协商且 CON 丢失的分块 GET
CLIENT                                                     SERVER
| |
| CON [MID=1234], GET, /status ------> |
| |
| <------ ACK [MID=1234], 2.05 Content, 2:0/1/128 |
| |
| CON [MID=1235], GET, /status, 2:2/0/64 ------> |
| |
| //////////////////////////////////tent, 2:2/1/64 |
| |
| (timeout) |
| |
| CON [MID=1235], GET, /status, 2:2/0/64 ------> |
| |
| <------ ACK [MID=1235], 2.05 Content, 2:2/1/64 |
: :
: ... :
: :
| CON [MID=1238], GET, /status, 2:5/0/64 ------> |
| |
| <------ ACK [MID=1238], 2.05 Content, 2:5/0/64 |

Figure 6: 带后期协商且 ACK 丢失的分块 GET

3.2. Block1 示例 (Block1 Examples)

以下示例演示 PUT 交换; POST 交换看起来相同, 只是对原子性/幂等性有不同要求. 注意, 与 GET 类似, 对请求 Block1 Option 中设置了 more bit 的请求的响应是临时响应, 并携带响应码 2.31 (Continue); 只有最终响应才会告诉客户端 PUT 已成功.

CLIENT                                                     SERVER
| |
| CON [MID=1234], PUT, /options, 1:0/1/128 ------> |
| |
| <------ ACK [MID=1234], 2.31 Continue, 1:0/1/128 |
| |
| CON [MID=1235], PUT, /options, 1:1/1/128 ------> |
| |
| <------ ACK [MID=1235], 2.31 Continue, 1:1/1/128 |
| |
| CON [MID=1236], PUT, /options, 1:2/0/128 ------> |
| |
| <------ ACK [MID=1236], 2.04 Changed, 1:2/0/128 |

Figure 7: 简单原子分块 PUT

简单地就地构建/更新资源 (以无状态方式) 的无状态服务器, 可以通过不在响应中设置 more bit 来表明这一点 (Figure 8); 在这种情况下, 响应码分别对每个正在更新的块有效. 当然, 只有在消息交换序列运行期间可能存在的不一致不会导致问题时, 这才是服务器可接受的行为. 例如, 被创建或更改的资源尚未使用或当前未使用.

CLIENT                                                     SERVER
| |
| CON [MID=1234], PUT, /options, 1:0/1/128 ------> |
| |
| <------ ACK [MID=1234], 2.04 Changed, 1:0/0/128 |
| |
| CON [MID=1235], PUT, /options, 1:1/1/128 ------> |
| |
| <------ ACK [MID=1235], 2.04 Changed, 1:1/0/128 |
| |
| CON [MID=1236], PUT, /options, 1:2/0/128 ------> |
| |
| <------ ACK [MID=1236], 2.04 Changed, 1:2/0/128 |

Figure 8: 简单无状态分块 PUT

最后, 接收分块 PUT 或 POST 的服务器可能希望指示更小的块大小偏好 (Figure 9). 在这种情况下, 客户端应当 (SHOULD) 使用更小的块大小继续; 如果这样做, 它必须 (MUST) 调整块编号, 以便按该更小大小正确计数.

CLIENT                                                     SERVER
| |
| CON [MID=1234], PUT, /options, 1:0/1/128 ------> |
| |
| <------ ACK [MID=1234], 2.31 Continue, 1:0/1/32 |
| |
| CON [MID=1235], PUT, /options, 1:4/1/32 ------> |
| |
| <------ ACK [MID=1235], 2.31 Continue, 1:4/1/32 |
| |
| CON [MID=1236], PUT, /options, 1:5/1/32 ------> |
| |
| <------ ACK [MID=1235], 2.31 Continue, 1:5/1/32 |
| |
| CON [MID=1237], PUT, /options, 1:6/0/32 ------> |
| |
| <------ ACK [MID=1236], 2.04 Changed, 1:6/0/32 |

Figure 9: 带协商的简单原子分块 PUT

3.3. 组合 Block1 和 Block2 (Combining Block1 and Block2)

Block 选项可以在单次交换的两个方向上使用. 以下示例演示一个分块 POST 请求, 它会产生一个单独的分块响应.

CLIENT                                                     SERVER
| |
| CON [MID=1234], POST, /soap, 1:0/1/128 ------> |
| |
| <------ ACK [MID=1234], 2.31 Continue, 1:0/1/128 |
| |
| CON [MID=1235], POST, /soap, 1:1/1/128 ------> |
| |
| <------ ACK [MID=1235], 2.31 Continue, 1:1/1/128 |
| |
| CON [MID=1236], POST, /soap, 1:2/0/128 ------> |
| |
| <------ ACK [MID=1236], 2.04 Changed, 2:0/1/128, 1:2/0/128 |
| |
| CON [MID=1237], POST, /soap, 2:1/0/128 ------> |
| (no payload for requests with Block2 with NUM != 0) |
| (could also do late negotiation by requesting, |
| e.g., 2:2/0/64) |
| |
| <------ ACK [MID=1237], 2.04 Changed, 2:1/1/128 |
| |
| CON [MID=1238], POST, /soap, 2:2/0/128 ------> |
| |
| <------ ACK [MID=1238], 2.04 Changed, 2:2/1/128 |
| |
| CON [MID=1239], POST, /soap, 2:3/0/128 ------> |
| |
| <------ ACK [MID=1239], 2.04 Changed, 2:3/0/128 |

Figure 10: 带分块响应的原子分块 POST

如下所示, 此模型确实可为 Block2 分块传输提供早期协商输入.

CLIENT                                                     SERVER
| |
| CON [MID=1234], POST, /soap, 1:0/1/128 ------> |
| |
| <------ ACK [MID=1234], 2.31 Continue, 1:0/1/128 |
| |
| CON [MID=1235], POST, /soap, 1:1/1/128 ------> |
| |
| <------ ACK [MID=1235], 2.31 Continue, 1:1/1/128 |
| |
| CON [MID=1236], POST, /soap, 1:2/0/128, 2:0/0/64 ------> |
| |
| <------ ACK [MID=1236], 2.04 Changed, 1:2/0/128, 2:0/1/64 |
| |
| CON [MID=1237], POST, /soap, 2:1/0/64 ------> |
| (no payload for requests with Block2 with NUM != 0) |
| |
| <------ ACK [MID=1237], 2.04 Changed, 2:1/1/64 |
| |
| CON [MID=1238], POST, /soap, 2:2/0/64 ------> |
| |
| <------ ACK [MID=1238], 2.04 Changed, 2:2/1/64 |
| |
| CON [MID=1239], POST, /soap, 2:3/0/64 ------> |
| |
| <------ ACK [MID=1239], 2.04 Changed, 2:3/0/64 |

Figure 11: 带分块响应和早期协商的原子分块 POST

3.4. 组合 Observe 和 Block2 (Combining Observe and Block2)

在以下示例中, 服务器先对初始 GET 请求发送直接响应 (Observe 序列号 62350); 由此产生的分块传输与 Figure 4 相同, 因而省略. 第二次传输由仅包含第一个块的 2.05 通知启动 (Observe 序列号 62354); 随后客户端继续获取其余块.

    CLIENT  SERVER
| |
+----->| Header: GET 0x41011636
| GET | Token: 0xfb
| | Uri-Path: status-icon
| | Observe: (empty)
| |
|<-----+ Header: 2.05 0x61451636
| 2.05 | Token: 0xfb
| | Block2: 0/1/128
| | Observe: 62350
| | ETag: 6f00f38e
| | Payload: [128 bytes]
| |
| | (Usual GET transfer left out)
...
| | (Notification of first block)
| |
|<-----+ Header: 2.05 0x4145af9c
| 2.05 | Token: 0xfb
| | Block2: 0/1/128
| | Observe: 62354
| | ETag: 6f00f392
| | Payload: [128 bytes]
| |
+- - ->| Header: 0x6000af9c
| |
| | (Retrieval of remaining blocks)
| |
+----->| Header: GET 0x41011637
| GET | Token: 0xfc
| | Uri-Path: status-icon
| | Block2: 1/0/128
| |
|<-----+ Header: 2.05 0x61451637
| 2.05 | Token: 0xfc
| | Block2: 1/1/128
| | ETag: 6f00f392
| | Payload: [128 bytes]
| |
+----->| Header: GET 0x41011638
| GET | Token: 0xfc
| | Uri-Path: status-icon
| | Block2: 2/0/128
| |
|<-----+ Header: 2.05 0x61451638
| 2.05 | Token: 0xfc
| | Block2: 2/0/128
| | ETag: 6f00f392
| | Payload: [53 bytes]

Figure 12: 带分块响应的 Observe 序列

注意, 本示例中选择 token 0xfc 是任意的; 示例中显示 token 只是为了说明, 对额外块的请求不能使用 Observation 关系的 token. 关于 token 的一般说明是, 本文档没有其他关于 token 的提及, 因为分块传输像任何其他 CoAP 交换一样处理 token. 和通常一样, 客户端可以按自己的需要为每次交换自由选择 token.

在以下示例中, 客户端还使用早期协商将块大小限制为 64 字节.

    CLIENT  SERVER
| |
+----->| Header: GET 0x41011636
| GET | Token: 0xfb
| | Uri-Path: status-icon
| | Observe: (empty)
| | Block2: 0/0/64
| |
|<-----+ Header: 2.05 0x61451636
| 2.05 | Token: 0xfb
| | Block2: 0/1/64
| | Observe: 62350
| | ETag: 6f00f38e
| | Max-Age: 60
| | Payload: [64 bytes]
| |
| | (Usual GET transfer left out)
...
| | (Notification of first block)
| |
|<-----+ Header: 2.05 0x4145af9c
| 2.05 | Token: 0xfb
| | Block2: 0/1/64
| | Observe: 62354
| | ETag: 6f00f392
| | Payload: [64 bytes]
| |
+- - ->| Header: 0x6000af9c
| |
| | (Retrieval of remaining blocks)
| |
+----->| Header: GET 0x41011637
| GET | Token: 0xfc
| | Uri-Path: status-icon
| | Block2: 1/0/64
| |
|<-----+ Header: 2.05 0x61451637
| 2.05 | Token: 0xfc
| | Block2: 1/1/64
| | ETag: 6f00f392
| | Payload: [64 bytes]
....
| |
+----->| Header: GET 0x41011638
| GET | Token: 0xfc
| | Uri-Path: status-icon
| | Block2: 4/0/64
| |
|<-----+ Header: 2.05 0x61451638
| 2.05 | Token: 0xfc
| | Block2: 4/0/64
| | ETag: 6f00f392
| | Payload: [53 bytes]

Figure 13: 带早期协商的 Observe 序列