跳到主要内容

3. 客户端要求 (Client-Side Requirements)

3.1. 请求 (Request)

客户端通过发出带有 Observe Option 且其值设置为 0 (register) 的 GET 请求, 来注册其对某个资源的兴趣. 如果服务器返回的 2.xx 响应也包含 Observe Option, 则表示服务器已成功将包含客户端端点和请求令牌的条目添加到目标资源的观察者列表中, 客户端将收到资源状态变化的通知.

正如新鲜响应可以在不联系服务器的情况下用于满足请求一样, 一个观察请求产生的更新流也可以用于满足另一个请求 (观察请求或普通 GET 请求), 前提是目标资源相同. 客户端必须聚合此类请求, 并且禁止针对同一目标资源注册多次. 目标资源由请求中属于 Cache-Key 的所有选项标识. 例如, 这包括完整的请求 URI 和 Accept Option.

3.2. 通知 (Notifications)

通知是服务器为响应创建注册的单个扩展 GET 请求而发送的额外响应. 每个通知都包含客户端在请求中指定的令牌. 通知与普通响应的唯一区别是存在 Observe Option.

通知通常具有 2.05 (Content) 响应码. 它们包含带有序列号的 Observe Option, 用于重排序检测 (见 Section 3.4), 并携带与初始响应相同 Content-Format 的负载. 如果客户端在 GET 请求中包含一个或多个 ETag Option (见 Section 3.3), 通知可以具有 2.03 (Valid) 响应码, 而不是 2.05 (Content) 响应码. 此类通知包含带有序列号的 Observe Option, 但不包含负载.

如果资源以某种方式发生变化, 导致此时的普通 GET 请求会返回非 2.xx 响应 (例如资源被删除), 服务器会发送一个带有适当响应码的通知 (例如 4.04 Not Found), 并从该资源的观察者列表中移除客户端条目. 非 2.xx 响应不包含 Observe Option.

3.3. 缓存 (Caching)

由于通知只是 GET 请求的额外响应, 通知会按照 RFC 7252 [RFC7252] Section 5.6 中的定义参与缓存. 新鲜度模型和验证模型都受支持.

3.3.1. 新鲜度 (Freshness)

客户端可以像存储响应一样在其缓存中存储通知, 并在不联系服务器的情况下使用仍然新鲜的已存储通知. 与响应一样, 只要通知的年龄不大于 Max-Age Option 指示的值 (且没有收到更新的通知/响应), 该通知就被视为新鲜.

服务器会尽最大努力让客户端观察到的资源状态尽可能接近实际状态. 但是, 客户端不能依赖于观察资源可能经历的每一个状态. 例如, 如果网络拥塞, 或状态变化频率超过网络可承载能力, 服务器可以跳过任意数量的中间状态通知.

服务器使用 Max-Age Option 指示一个年龄上限, 在此上限内允许观察到的状态与实际状态不一致. 如果最新通知的年龄超过其指示的 Max-Age, 则客户端禁止假定其中包含的表示反映实际资源状态.

为了确保拥有当前表示和/或重新注册其对资源的兴趣, 客户端可以在任何时候使用与原始请求相同的令牌发出新的 GET 请求. 除 ETag Option 集合外, 所有选项必须与原始请求中的选项完全相同. 推荐客户端在其缓存中仍有该资源的新鲜通知/响应时不要发出该请求. 此外, 客户端应当至少在 Max-Age 过期后等待 5 到 15 秒之间的随机时长, 以减少与其他客户端的冲突.

3.3.2. 验证 (Validation)

当客户端的缓存中存有某个资源的一个或多个通知时, 它可以在 GET 请求中使用 ETag Option, 使服务器有机会选择一个已存储通知来使用.

客户端可以在 GET 请求中为每个适用的已存储响应包含一个 ETag Option. 每当被观察资源变化为由其中一个 ETag Option 标识的表示时, 服务器可以发送带有适当 ETag Option 的 2.03 (Valid) 通知来选择一个已存储响应, 而不是发送 2.05 (Content) 通知.

客户端实现需要将所有候选响应保留在其缓存中, 直到它不再对目标资源感兴趣, 或使用新的实体标签集合重新注册.

3.4. 重排序 (Reordering)

携带通知的消息可能以不同于发送顺序的顺序到达. 由于目标是让观察到的状态尽可能接近实际状态, 客户端必须将最近发送的通知视为最新鲜的通知, 而不考虑到达顺序.

为了给客户端提供通知之间的顺序, 服务器将每个通知中 Observe Option 的值设置为严格递增序列号的最低 24 位. 当满足以下任一条件时, 传入通知比目前最新鲜的通知发送得更晚:

(V1 < V2 and V2 - V1 < 2^23) or
(V1 > V2 and V1 - V2 > 2^23) or
(T2 > T1 + 128 seconds)

其中 V1 是目前最新鲜通知中的 Observe Option 值, V2 是传入通知中的 Observe Option 值, T1 是目前最新鲜通知的客户端本地时间戳, T2 是传入通知的客户端本地时间戳.

设计说明: 前两个条件验证在 24 位序列号算术 [RFC1982] 中 V1 小于 V2. 第三个条件确保, 如果服务器基于本地时钟生成序列号, 两个传入消息之间经过的时间不会大到使 V1 和 V2 之间的差值超过对 24 位序列号有意义的最大可加整数. 换言之, 在 128 秒内没有任何通知之后, 客户端不需要检查序列号, 就可以假定传入通知比它目前收到的最新鲜通知发送得更晚.

128 秒这个持续时间被选为一个大于 MAX_LATENCY (RFC 7252 [RFC7252] Section 4.8.2) 的便于使用的整数.

3.5. 传输 (Transmission)

通知可以是可确认的, 也可以是不可确认的, 即它可以在可确认消息或不可确认消息中发送. 通知使用的消息类型独立于请求使用的类型, 也独立于任何先前通知使用的类型.

如果客户端无法识别可确认通知中的令牌, 它禁止确认该消息, 并且应当使用 Reset 消息拒绝该消息; 否则, 客户端必须照常确认该消息. 对于不可确认通知, 使用 Reset 消息拒绝该消息是可选的.

确认消息向服务器表明客户端仍然存活, 且有兴趣继续接收通知; 如果服务器没有收到对可确认通知的确认, 它将假定客户端不再感兴趣, 并最终从观察者列表中移除关联条目 (Section 4.5).

3.6. 取消 (Cancellation)

不再有兴趣接收某个资源通知的客户端可以简单地 "忘记" 该观察. 当服务器随后发送下一个通知时, 客户端无法识别消息中的令牌, 因而会返回 Reset 消息. 这会使服务器从观察者列表中移除关联条目. 观察者列表中的条目实际上由服务器进行 "垃圾回收".

实现说明: 由于消息可能丢失, Reset 消息可能无法到达服务器. 因此, 客户端可能必须拒绝多个通知, 每个通知使用一个 Reset 消息, 直到服务器最终从观察者列表中移除关联条目并停止发送通知.

在某些情况下, 可能希望更主动地取消观察并释放服务器为其分配的资源. 在这种情况下, 客户端可以通过发出 GET 请求显式注销, 该请求的 Token 字段设置为要取消的观察的令牌, 并包含值设置为 1 (deregister) 的 Observe Option. 除 ETag Option 集合外, 所有其他选项必须与注册请求中的选项完全相同. 当服务器收到这样的请求时, 它会从观察者列表中移除任何匹配条目, 并照常处理该 GET 请求.