跳到主要内容

4. 服务器侧要求 (Server-Side Requirements)

4.1. 请求 (Request)

带有 Observe Option 且其值设置为 0 (register) 的 GET 请求, 要求服务器不仅返回目标资源的当前表示, 还将客户端添加到该资源的观察者列表中. 成功时, 服务器返回资源的当前表示, 并且只要客户端位于观察者列表中, 服务器就必须保持该表示持续更新 (如 Section 1.3 所述).

观察者列表中的条目由客户端端点和客户端在请求中指定的令牌作为键. 如果列表中已经存在匹配的端点/令牌对条目 (例如客户端希望加强其对资源的兴趣时会发生这种情况), 服务器禁止添加新条目, 而必须替换或更新现有条目.

如果服务器无法或不愿向某个资源的观察者列表添加新条目, 它可以静默忽略注册请求, 并照常处理 GET 请求. 生成的响应禁止包含 Observe Option, 该选项缺失会向客户端表明它不会收到资源变化通知, 因而例如需要改为轮询资源状态.

如果 GET 请求中的 Observe Option 设置为 1 (deregister), 则服务器必须从观察者列表中移除任何匹配端点/令牌对的现有条目, 并照常处理 GET 请求. 生成的响应禁止包含 Observe Option.

4.2. 通知 (Notifications)

服务器通过针对 GET 请求发送额外响应, 将资源状态变化通知给客户端. 每个这样的通知响应 (包括初始响应) 都必须回显客户端在 GET 请求中指定的令牌. 如果观察者列表中有多个条目, 客户端收到通知的顺序未定义; 服务器可以自由使用任何方法确定顺序.

通知应当具有 2.05 (Content) 或 2.03 (Valid) 响应码. 但是, 如果资源状态的变化会导致此时普通 GET 请求返回非 2.xx 响应 (例如资源被删除), 则服务器应当通过发送带有适当响应码的通知 (例如 4.04 Not Found) 来通知客户端, 随后必须从该资源的观察者列表中移除关联条目.

2.xx 通知中指定的 Content-Format 必须与 GET 请求的初始响应中使用的 Content-Format 相同. 如果服务器无法继续以此格式发送通知, 它应当发送带有 4.06 (Not Acceptable) 响应码的通知, 随后必须从该资源的观察者列表中移除关联条目.

2.xx 通知必须包含带有序列号的 Observe Option, 如下面 Section 4.4 所指定; 非 2.xx 通知禁止包含 Observe Option.

4.3. 缓存 (Caching)

由于通知只是服务器为响应 GET 请求而发送的额外响应, 它们会按照 RFC 7252 [RFC7252] Section 5.6 中的定义受缓存处理.

4.3.1. 新鲜度 (Freshness)

返回初始响应后, 服务器必须让客户端观察到的资源状态尽可能与实际资源状态保持同步.

由于有时无法避免失去同步, 服务器必须为每个表示指示一个年龄上限, 在此年龄内允许观察到的状态与实际状态不一致. 该年龄取决于应用, 并且必须在通知中使用 Max-Age Option 指定.

当资源没有变化且客户端拥有当前表示时, 服务器不需要发送通知. 但是, 如果客户端没有收到通知, 客户端无法判断观察到的状态与实际状态是否仍然同步. 因此, 当最新通知的年龄大于其指示的 Max-Age 时, 客户端不再拥有可用的资源状态表示. 服务器可以在先前指示的 Max-Age 到期前发送一个带有未变化表示和新 Max-Age 的新通知, 以避免这种情况.

4.3.2. 验证 (Validation)

客户端可以使用 ETag Option 在其请求中包含一组实体标签. 当被观察资源改变状态且源服务器即将发送 2.05 (Content) 通知时, 如果该通知的实体标签属于客户端指定的实体标签集合, 服务器可以改为发送带有适当 ETag Option 的 2.03 (Valid) 响应.

4.4. 重排序 (Reordering)

由于消息可能被重排序, 客户端需要一种方法来确定某个通知是否晚于一个更新的通知到达. 为此, 服务器必须将其发送的每个通知中的 Observe Option 值设置为严格递增序列号的最低 24 位. 序列号可以从任意值开始, 并且禁止增长得过快, 即禁止在少于 256 秒内增长超过 2^23.

为通知选择的序列号必须大于此前以相同令牌发送给同一客户端且针对同一资源的任何通知的序列号. Observe Option 的值必须在传输时为当前值; 如果重传某个通知, 服务器必须在重传前将该选项的值更新为当时当前的序列号.

实现说明: 满足这些要求的一种简单实现是从本地时钟获取时间戳. 序列号随后就是以 tick 为单位的时间戳, 其中 1 tick = (256 seconds)/(2^23) = 30.52 microseconds. 该时钟不必反映当前时间/日期.

另一种有效实现是为每个资源存储一个 24 位无符号整数变量, 并在该资源每次发生状态变化时递增该变量 (前提是资源在每次状态变化后的最初 256 秒内状态变化少于 2^23 次). 当资源状态未变化时, 这种方式消除了在重传时更新 Observe Option 值的需要.

设计说明: 选择 24 位选项值和 256 秒时间跨度, 理论上允许通知速率最高达到每秒 65536 个通知. 但是, 受限节点的时钟通常相当不精确, 客户端侧和服务器侧的不准确性在效果上可能相互抵消, 也可能叠加. 因此, 最大通知速率降低到每秒 32768 个通知. 这仍远高于已知最高设计目标, 约为 1 kHz (大多数 CoAP 应用会低几个数量级), 但允许总时钟不准确性最高达到 -50/+100%.

4.5. 传输 (Transmission)

通知可以在可确认消息或不可确认消息中发送. 使用的消息类型通常取决于应用, 并且可以由服务器针对每个通知单独确定.

例如, 对于以某种可预测或规律方式变化的资源, 通知可以在不可确认消息中发送; 对于不频繁变化的资源, 通知可以在可确认消息中发送. 服务器可以根据状态变化频率和单个通知的重要性组合这两种方法.

如果服务器知道很快会发送另一个通知, 例如资源状态频繁变化时, 它可以选择跳过发送某个通知. 它也可以选择为同一资源状态发送多个通知. 但是最重要的是, 服务器必须确保资源观察者列表中的客户端在资源没有发生新的状态变化时最终观察到最新状态.

例如, 当状态变化成批发生时, 服务器可以跳过一些通知, 在不可确认消息中发送通知, 并在突发结束时通过在可确认消息中重复最后一个通知, 确保客户端观察到最新状态变化.

客户端对可确认通知的确认表明客户端有兴趣继续接收通知. 如果客户端使用 Reset 消息拒绝可确认或不可确认通知, 或者可确认通知的最后一次重传尝试超时, 则客户端被视为不再感兴趣, 服务器必须从观察者列表中移除关联条目.

实现说明: 为了正确处理拒绝不可确认通知的 Reset 消息, 服务器需要记住它发送的不可确认通知的消息 ID. 对资源受限的服务器来说, 这可能具有挑战. 但是, 由于 Reset 消息是不可靠传输的, 客户端必须准备好处理 Reset 消息未被服务器接收的情况. 因此, 服务器始终可以假装拒绝不可确认通知的 Reset 消息丢失了.

如果服务器这样做, 它可以通过在可确认消息中向该客户端发送后续通知来加速取消.

主要在不可确认消息中传输通知的服务器, 必须至少每 24 小时改为在可确认消息中发送一个通知, 而不是发送不可确认消息. 这可以防止已离开或不再感兴趣的客户端无限期留在观察者列表中.

4.5.1. 拥塞控制 (Congestion Control)

CoAP 的基本拥塞控制由 RFC 7252 [RFC7252] Section 4.2 中的指数退避机制以及 RFC 7252 [RFC7252] Section 4.7 中的限制提供. 但是, 对简单请求/响应交互而言, CoAP 只把拥塞控制责任放在客户端上: 对请求传输进行速率限制会隐式控制响应传输. 当单个请求产生潜在无限数量的通知时, 服务器需要承担额外责任.

为了不造成拥塞, 服务器必须严格限制它向给定客户端同时传输的未完成通知/响应数量, 该数量限制为 NSTART (默认为 1; 见 RFC 7252 [RFC7252] Section 4.7). 未完成通知/响应指以下两者之一: 尚未收到确认且最后一次重传尝试尚未超时的可确认消息, 或由下列速率限制规则产生的等待时间尚未经过的不可确认消息.

服务器平均不应向某个客户端在每个往返时间 (Round-Trip Time, RTT) 内发送超过一个不可确认通知. 如果服务器无法维护某个客户端的 RTT 估计, 它不应每 3 秒向该客户端发送超过一个不可确认通知, 并且在可能时应使用更不激进的速率 (另见 RFC 5405 [RFC5405] Section 3.1.2).

未来预计会在高级 CoAP 拥塞控制机制中提供进一步的拥塞控制优化和考虑事项.

4.5.2. 高级传输 (Advanced Transmission)

当面向观察者列表中某个客户端的同时未完成通知/响应数量大于或等于 NSTART 时, 被观察资源的状态可能发生变化. 在这种情况下, 服务器无法立即将新的资源状态通知给客户端, 而必须先等待一个未完成通知/响应完成.

如果存在一个由服务器传输给该客户端且与变化资源相关的未完成通知/响应, 则服务器最好停止继续尝试把旧资源状态的表示送达客户端, 而改为开始向客户端传输当前表示, 这样客户端观察到的资源状态会更接近服务器上的实际状态.

为此, 服务器可以通过中止旧通知的传输 (但不能早于当前传输尝试完成时) 并开始传输新通知来优化传输过程 (但保留被中止传输的重传计时器和计数器).

更详细地说, 服务器可以按如下方式取代一个与观察相关的未完成传输: