2.3 连接握手

常量定义

  • handshake code
    • 0: 握手成功
    • 1: 认证失败,握手失败
    • -1: 握手失败

connect

服务器需要对连接的客户端进行认证,UDPack 协议提供了一种简单的 token 认证机制,客户端发送握手请求时携带 token,服务端收到 token 后进行验证,如果合法则继续处理握手请求,否则拒绝连接。客户端创建 UDPack 对象时传入 token。服务端创建 UDPack 对象时,需注册由上层应用程序自定义的 token 验证函数,服务端可通过此函数验证 token 是否合法。

UDPack 是安全传输协议,需要采用对称加密算法对数据进行加密后再传输,因此在创建 Session 的握手过程中,需要协商加密算法及相关参数。为了保持协议简单,客户端和服务端可支持相同加密算法,并由客户端确定使用哪个算法。同时,客户端随机生成加密算法所需其他参数,如 密钥(key)、初始向量(iv) 等。客户端发送握手请求时,携带加密算法及相关参数(algorithm,key,iv)。

为了安全传递上述敏感信息,客户端需事先集成服务端的公钥。服务端创建 UDPack 对象时,传入服务端公钥及私钥。客户端创建 UDPack 对象时,传入服务端公钥。客户端使用服务端的公钥对上述敏感信息进行加密,然后传递给服务端。服务端通过私钥解密数据,获得对称加密算法及参数。

连接握手过程如下:

客户端与服务端分别打开 UDP 套接字,并绑定端口。客户端首先发起连接请求,创建本地 Session ,状态为 OPEN,并发出 shakehand 帧

  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
  +-------------------------------+-------------------------------+
  |        [random integer]       |               [0]             |
  +-------------------------------+-------------------------------+
  |                        timestamp ...                          |
  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
  |                    ... timestamp [curent time]                |
  +-+-+-+-----+-+-+-------+-------+-------------------------------+
  |S|P|F| RSV |S|F|  RSV  |OPCODE |          Payload len          |
  |T|A|R| [0] |L|I|  [0]  |[0xA]  |        [Payload.length]       |
  |R|C|A|     |O|N|       |       |                               |
  |E|K|G|     |W| |       |       |                               |
  |0|0|0|     |0|0|       |       |                               |
  +-+-+-+-----+-+-+-------+-------+-------------------------------+
  :              Payload [algorithm,key,iv,token]                 :
  +---------------------------------------------------------------+

客户端需要随机生成一个 16 位无符号整数,写入 Session ID 字段的高 16 位。低 16 位由服务器生成,此时写入 0 即可。timestamp 字段写入当前时间戳,精确到毫秒。STRE、PACK、FRAG、SLOW、FIN 设置为 0。RSV 字段设置为 0。OPCODE 字段设置为 0xA。

使用服务端公钥加密 [algorithm,key,iv,token] 并写入 Payload 字段,计算 Payload 的字节数写入 Payload len 字段。

服务端收到 shakehand 帧后,通过服务端私钥解密 Payload,解析出加密算法及参数和 token,验证 token,如果合法,则创建 Session,状态为 OPEN,并回复 handshake 帧

  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
  +-------------------------------+-------------------------------+
  |        [id from client]       |       [random integer]        |
  +-------------------------------+-------------------------------+
  |                        timestamp ...                          |
  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
  |                    ... timestamp [curent time]                |
  +-+-+-+-----+-+-+-------+-------+-------------------------------+
  |S|P|F| RSV |S|F|  RSV  |OPCODE |          Payload len          |
  |T|A|R| [0] |L|I|  [0]  |[0xB]  |              [1]              |
  |R|C|A|     |O|N|       |       |                               |
  |E|K|G|     |W| |       |       |                               |
  |0|0|0|     |0|0|       |       |                               |
  +-+-+-+-----+-+-+-------+-------+-------------------------------+
  |Payload [code] |
  +---------------+

服务端需要随机生成一个 16 位无符号整数,写入 Session ID 字段的低 16 位。高 16 写入客户端传过来的值。timestamp 字段写入当前时间戳,精确到毫秒。接下来 STRE、PACK、FRAG、SLOW、FIN 设置为 0。RSV 字段设置为 0。OPCODE 字段设置为 0xB。Payload 写入代表握手成功的 code 值。code 为预先定义的 8 位无符号整数。Payload len 字段写入 1。

验证 token,如果非法,则回复 handshake 帧,Payload 写入代表认证失败的 code,Payload len 字段写入 1,其他字段设置如上所述。

客户端收到 handshake 帧后,读取 Payload 中的 code,认证失败则握手失败,认证成功,则读取服务端生成的 Session ID 的低 16 位,合并高、低 16 位组成 32 位 Session ID 写入 Session 对象,并将 Session 状态更新为 READY,然后回复 handshake 帧

  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
  +-------------------------------+-------------------------------+
  |        [id from client]       |       [id from server]        |
  +-------------------------------+-------------------------------+
  |                        timestamp ...                          |
  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
  |                    ... timestamp [curent time]                |
  +-+-+-+-----+-+-+-------+-------+-------------------------------+
  |S|P|F| RSV |S|F|  RSV  |OPCODE |          Payload len          |
  |T|A|R| [0] |L|I|  [0]  |[0xB]  |              [1]              |
  |R|C|A|     |O|N|       |       |                               |
  |E|K|G|     |W| |       |       |                               |
  |0|0|0|     |0|0|       |       |                               |
  +-+-+-+-----+-+-+-------+-------+-------------------------------+
  |Payload [code] |
  +---------------+

帧格式和服务端发送的 handshake 帧一样,其中 Payload 写入代表握手成功的 code,Payload len 写入 1。客户端将 Session 返回给上层应用程序。

服务端收到客户端发送的 handshake 帧后,将 Session 状态更新为 READY,此时连接已建立完成。服务端将 Session 返回给上层应用程序。

超时重试

一段时间后客户端检查本地 Session 对象状态,如果不是 READY 状态,说明没有连接成功,需要进行重试。可以通过选项控制重试次数,在重试次数达到上限后,如果仍然无法完成连接,则关闭会话。并上报上层应用程序连接超时错误。