11. 通道
Channel 提供了一种方式, 使客户端和服务器能够使用 ChannelData 消息发送应用数据. 与 Send 和 Data indication 相比, ChannelData 消息的开销更小.
ChannelData 消息 (见第 11.4 节) 以一个携带 channel number 的两字节字段开始. 该字段的取值分配如下:
-
0x0000到0x3FFF: 这些值永远不能用作 channel number. -
0x4000到0x7FFF: 这些值是允许的 channel number (16,383 个可能值). -
0x8000到0xFFFF: 这些值保留供将来使用.
由于这种划分, 可以通过检查消息的前两位来区分 ChannelData 消息和 STUN 格式的消息 (例如 Allocate request, Send indication 等):
-
0b00: STUN 格式的消息 (因为 STUN 格式消息的前两位始终为零). -
0b01: ChannelData 消息 (因为 channel number 是 ChannelData 消息中的第一个字段, 且 channel number 位于 0x4000 - 0x7FFF 范围内). -
0b10: 保留 -
0b11: 保留
这些保留值将来可能用于扩展 channel number 的范围. 因此, 实现不得假定 TURN 消息总是以 0 位开头.
Channel binding 始终由客户端发起. 在 allocation 的生命周期内, 客户端可以随时将一个 channel 绑定到一个对等方. 客户端可以在与对等方交换数据之前将 channel 绑定到该对等方, 也可以在使用 Send 和 Data indication 交换一段时间数据之后再绑定, 或者选择永远不绑定 channel. 客户端也可以为某些对等方绑定 channel, 而不为其他对等方绑定 channel.
Channel binding 特定于某个 allocation, 因此一个 allocation 的 channel binding 中使用的 channel number 或对等方传输地址, 不会影响它们在另一个 allocation 的 channel binding 中的使用. 如果 allocation 过期, 它的所有 channel binding 都随之过期.
一个 channel binding 包含:
-
一个 channel number.
-
一个 (对等方的) 传输地址.
-
一个 time-to-expiry 计时器.
在一个 allocation 的上下文中, channel binding 可以由 channel number 唯一标识, 也可以由对等方的传输地址唯一标识. 因此, 同一个 channel 不能绑定到两个不同的传输地址, 同一个传输地址也不能绑定到两个不同的 channel.
除非刷新, channel binding 会持续 10 分钟. 刷新 binding (即服务器收到一个将该 channel 重新绑定到同一对等方的 ChannelBind request) 会将 time-to-expiry 计时器重置为 10 分钟.
当 channel binding 过期时, channel 变为未绑定状态. 一旦解除绑定, channel number 可以绑定到另一个传输地址, 该传输地址也可以绑定到另一个 channel number. 为了防止竞态条件, 客户端必须在 channel binding 过期后等待 5 分钟, 然后才能尝试将该 channel number 绑定到另一个传输地址, 或将该传输地址绑定到另一个 channel number.
将 channel 绑定到对等方时, 客户端应该在发送 ChannelBind request 后立即准备好在该 channel 上接收来自服务器的 ChannelData 消息. 在 UDP 上, 客户端可以在收到 ChannelBind success response 之前就收到来自服务器的 ChannelData 消息.
在另一个方向上, 客户端可以选择在收到 ChannelBind success response 之前发送 ChannelData 消息. 但是, 如果 ChannelBind request 由于某种原因未成功 (例如请求通过 UDP 发送时发生丢包, 或服务器无法满足该请求), 这样做会有 ChannelData 消息被服务器丢弃的风险. 希望稳妥处理的客户端应当在 channel binding 得到确认之前排队保存数据, 或者使用 Send indication.
11.1. 发送 ChannelBind 请求
ChannelBind transaction 用于创建或刷新 channel binding. ChannelBind transaction 还会创建或刷新指向对等方的 permission (见第 8 节).
为了发起 ChannelBind transaction, 客户端构造一个 ChannelBind request. 要绑定的 channel 在 CHANNEL-NUMBER 属性中指定, 对等方的传输地址在 XOR-PEER-ADDRESS 属性中指定. 第 11.2 节描述了这些属性上的限制.
将 channel 重新绑定到它已经绑定的同一个传输地址, 提供了一种无需向对等方发送数据即可刷新 channel binding 及其对应 permission 的方式. 但是请注意, permission 需要比 channel 更频繁地刷新.
11.2. 接收 ChannelBind 请求
当服务器收到 ChannelBind request 时, 它按照第 4 节以及此处提到的特定规则进行处理.
服务器检查以下事项:
-
请求同时包含 CHANNEL-NUMBER 属性和 XOR-PEER-ADDRESS 属性.
-
channel number 位于 0x4000 到 0x7FFE (含) 范围内.
-
channel number 当前未绑定到另一个传输地址 (同一个传输地址可以).
-
传输地址当前未绑定到另一个 channel number.
如果这些测试中任一项失败, 服务器以 400 (Bad Request) 错误响应.
服务器可以对 XOR-PEER-ADDRESS 属性中允许的 IP 地址和端口值施加限制 -- 如果某个值不被允许, 服务器以 403 (Forbidden) 错误拒绝该请求.
如果请求有效, 但服务器由于某种容量限制或类似原因无法满足该请求, 则服务器以 508 (Insufficient Capacity) 错误响应.
否则, 服务器以 ChannelBind success response 响应. 成功的 ChannelBind response 中没有必需属性.
如果服务器能够满足该请求, 则服务器使用 CHANNEL-NUMBER 属性中的 channel number 和 XOR-PEER-ADDRESS 属性中的传输地址创建或刷新 channel binding. 服务器还会按照第 8 节所述, 为 XOR-PEER-ADDRESS 属性中的 IP 地址安装或刷新 permission.
注: 使用 "stateless stack approach" 时, 服务器无需做任何特殊处理即可实现 UDP 上 ChannelBind request 的幂等性. 重传的 ChannelBind request 只会刷新 channel binding 和相应的 permission. 此外, 客户端必须等待 5 分钟后才能将先前已绑定的 channel number 或对等方地址绑定到另一个 channel, 这消除了 transaction 初次失败但在重传时成功的可能性.
11.3. 接收 ChannelBind 响应
当客户端收到 ChannelBind success response 时, 它会更新其数据结构, 记录该 channel binding 现在处于活动状态. 它还会更新其数据结构, 记录相应的 permission 已安装或已刷新.
如果客户端收到的 ChannelBind failure response 表明客户端与服务器之间的 channel 信息不同步 (例如意外的 400 "Bad Request" 响应), 则推荐客户端立即删除该 allocation, 并使用新的 allocation 重新开始.
11.4. ChannelData 消息
ChannelData 消息用于在客户端和服务器之间承载应用数据. 其格式如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Channel Number | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
/ Application Data /
/ /
| |
| +-------------------------------+
| |
+-------------------------------+
Channel Number 字段指定数据传输所使用的 channel number, 因而也指定了正在发送数据或将接收数据的对等方地址.
Length 字段指定 Application Data 字段的长度, 单位为字节 (即不包括 ChannelData 头的大小). 注意, 0 是有效长度.
Application Data 字段承载客户端试图发送给对等方的数据, 或对等方发送给客户端的数据.
11.5. 发送 ChannelData 消息
一旦客户端已将 channel 绑定到某个对等方, 当客户端有数据要发送给该对等方时, 它可以使用 ChannelData 消息或 Send indication. 也就是说, 即使 channel 存在, 客户端也没有义务使用该 channel, 在向对等方发送数据时可以自由使用任一消息类型. 另一方面, 如果已有 channel 绑定到该对等方, 服务器必须使用 ChannelData 消息.
ChannelData 消息的各字段按第 11.4 节所述填充.
在 TCP 和 TLS-over-TCP 上, ChannelData 消息必须填充到四字节的倍数, 以确保后续消息对齐. 填充不会反映在 ChannelData 消息的 length 字段中, 因此 ChannelData 消息的实际大小 (包括填充) 是 (4 + Length) 向上舍入到最接近的 4 的倍数. 在 UDP 上, 不要求填充, 但可以包含填充.
随后在与 allocation 关联的 5-tuple 上发送 ChannelData 消息.
11.6. 接收 ChannelData 消息
ChannelData 消息的接收方使用前两位将其与 STUN 格式的消息区分开, 如上所述. 如果消息使用保留范围内的值 (0x8000 到 0xFFFF), 则静默丢弃该消息.
如果 ChannelData 消息是在 UDP 数据报中收到的, 且该 UDP 数据报太短, 不足以包含 ChannelData 消息声称承载的数据长度 (即 UDP 头部 length 字段的值小于 ChannelData 头部 length 字段 + 4 + 8), 则静默丢弃该消息.
如果 ChannelData 消息是通过 TCP 或 TLS-over-TCP 收到的, 则 ChannelData 消息的实际长度如第 11.5 节所述.
如果在一个未绑定到任何对等方的 channel 上收到 ChannelData 消息, 则静默丢弃该消息.
在客户端上, 如果客户端认为不存在指向该对等方的活动 permission, 则推荐客户端丢弃该 ChannelData 消息. 在服务器上, 收到 ChannelData 消息不得刷新 channel binding, 也不得刷新指向对等方的 permission.
在服务器上, 如果未检测到错误, 服务器会按如下方式构造 UDP 数据报, 将应用数据转发给对等方:
-
源传输地址是 allocation 的 relayed transport address, 其中 allocation 由接收该 ChannelData 消息的 5-tuple 确定.
-
目的传输地址是该 channel 绑定到的传输地址.
-
UDP 头之后的数据是 ChannelData 消息 data 字段的内容.
随后将生成的 UDP 数据报发送给对等方. 注意, 如果 ChannelData 消息中的 Length 字段为 0, 则 UDP 数据报中没有数据, 但仍会构造并发送该 UDP 数据报.
11.7. 转发来自对等方的数据
当服务器在与 allocation 关联的 relayed transport address 上收到 UDP 数据报时, 它按第 10.3 节所述进行处理. 如果该节指出应发送 ChannelData 消息 (因为存在一个 channel 绑定到发送该 UDP 数据报的对等方), 则服务器按第 11.5 节所述构造并发送 ChannelData 消息.