首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

WebSocket 协议(5)

WebSocket 协议(5)

一旦客户端和服务端的连接建立好(包括经由代理或者通过 TLS 加密隧道),客户端必须向服务端发送 WebSocket 握手信息。握手内容包括了 HTTP 升级请求和一些必选以及可选的头字段。握手的细节如下:
  • 握手必须是一个有效的 HTTP 请求,有效的 HTTP 请求的定义见  

  • 请求的方法必须是 GET,并且 HTTP 的版本必须至少是 1.1
    比如,如果 WebSocket 的 URI 是 ws://example.com/chat,那么请求的第一行必须是 GET /chat HTTP/1.1。
  • 请求的 Request-URI 部分必须遵循第 3 节中定义的 /resource name/ 的定义。可以使相对路径或者绝对路径,比如:
    相对路径:GET /chat HTTP/1.1 中间的 /chat 就是请求的 Request-URI,也是 /resource name/
    绝对路径:GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1,其中的 /resource name/ 就是 /pub/WWW/TheProject.html 感谢 @forl 的指正
    绝对路径解析之后会有 /resource name/,/host/ 或者可能会有 /port/。/resource name/ 可能会有查询参数的,只不过例子中没有。
  • 请求必须有一个 |Host| 头字段,它的值是 /host/ 主机名称加上 /port/ 端口名称(当不是使用的默认端口时必须显式的指明)
  • 请求必须有一个 |Upgrade| 头字段,它的值必须是 websocket 这个关键字(keyword)
  • 请求必须有一个 |Connection| 头字段,它的值必须是 Upgrade 这个标记(token)
  • 请求必须有一个 |Sec-WebSocket-Key| 头字段,它的值必须是一个噪音值,由 16 个字节的随机数经过 base64 编码而成。每个连接的噪音必须是不同且随机的。
    注意:作为一个例子,如果选择的随机 16 个字节的值是 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10,那么头字段中的值将是 AQIDBAUGBwgJCgsMDQ4PEC==
  • 如果连接来自浏览器客户端,那么 |Origin|   就是必须的。如果连接不是来自于一个浏览器客户端,那么这个值就是可选的。这个值表示的是发起连接的代码在运行时所属的源。关于源是由哪些部分组成的,见  。
    作为一个例子,如果代码是从 http://cdn.jquery.com 下载的,但是运行时所属的源是 http://example.com,如果代码向 ww2.example.com 发起连接,那么请求中 |Origin| 的值将是 http://example.com。
  • 请求必须有一个 |Sec-WebSocket-Version| 头字段,它的值必须是 13
  • 请求可以有一个可选的头字段 |Sec-WebSocket-Protocol|。如果包含了这个头字段,它的值表示的是客户端希望使用的子协议,按子协议的名称使用逗号分隔。组成这个值的元素必须是非空的字符串,并且取值范围在 U+0021 到 U+007E 之间,不可以包含定义在   的分隔字符(separator character),并且每个以逗号分隔的元素之间必须相互不重复。
  • 请求可以有一个可选的头字段 |Sec-WebSocket-Extensions|。如果包含了这个字段,它的值表示的是客户端希望使用的协议级别的扩展,具体的介绍以及它的格式在第 9 节
  • 请求可以包含其他可选的头字段,比如 cookies  ,或者认证相关的头字段,比如 |Authorization| 定义在  ,它们的处理方式就参照定义它们的技术说明中的描述。
一旦客户端的握手请求发送完成后,客户端必须等待服务端的握手响应,在此期间不可以向服务器传输任何数据。客户端必须按照下面的描述去验证服务端的握手响应:
  • 如果服务端传来的状态码不是 101,那么客户端可以按照一般的 HTTP 请求处理状态码的方式去处理。比如服务端传来 401 状态码,客户端可以执行一个授权验证;或者服务端回传的是 3xx 的状态码,那么客户端可以进行重定向(但是客户端不是非得这么做)。如果是 101 的话,就接着下面的步骤。
  • 如果服务端回传的握手中没有 |Upgrade| 头字段或者 |Upgrade| 都字段的值不是 ASCII 大小写不敏感的 websocket 的话,客户端必须标记 WebSocket 连接为失败。
  • 如果服务端回传的握手中没有 |Connection| 头字段或者 |Connection| 的头字段内容不是大小写敏感的 Upgrade 的话,客户端必须表示 WebSocket 连接为失败。
  • 如果服务端的回传握手中没有 |Sec-WebSocket-Accept| 头字段或者 |Sec-WebSocket-Accept| 头字段的内容不是 |Sec-WebSocket-Key| 的内容(字符串,不是 base64 解码后的)联结上字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 的字符串进行 SHA-1 得出的字节再 base64 编码得到的字符串的话,客户端必须标记 WebSocket 连接为失败。
    简单的说就是客户端也必须按照服务端生成 |Sec-WebSocket-Accept| 头字段值的方式也生成一个字符串,与服务端回传的进行对比,如果不同就标记连接为失败的。
  • 如果服务端回传的 |Sec-WebSocket-Extensions| 头字段的内容不是客户端握手请求中的扩展集合中的元素或者 null 的话,客户端必须标记连接为失败。这个头字段的解析规则在第 9 节中进行了描述。
    比如客户端的握手请求中的期望使用的扩展集合为:
    Sec-WebSocket-Extensions: bar; baz=2那么服务端可以选择使用其中的某个(些)扩展,通过在回传的 |Sec-WebSocket-Extensions| 头字段中表明:
    Sec-WebSocket-Extensions: bar; baz=2上面的服务端返回表示都使用。也可以使用其中的一个:
    Sec-WebSocket-Extensions: bar如果服务端希望表示一个都不使用,即表示 null,那么服务端回传的信息中将不可以包含 |Sec-WebSocket-Extensions|。
    失败的界定就是,如果客户端握手请求中有 |Sec-WebSocket-Extensions|,但是服务端返回的 |Sec-WebSocket-Extensions| 中包含了客户端请求中没有包含的值,那么必须标记连接为失败。服务端的返回中不包含 |Sec-WebSocket-Extensions| 是可以的,表示客户端和服务端之间将不使用任何扩展。
  • 如果客户端在握手请求中包含了子协议头字段 |Sec-WebSocket-Protocol|,其中的值表示客户端希望使用的子协议的集合。如果服务端回传信息的 |Sec-WebSocket-Protocol| 值不属于客户端握手请求中的子协议集合的话,那么客户端必须标记连接为失败。
如果服务端的握手响应不符合 4.2.2 小节中的服务端握手定义的话,客户端必须标记连接为失败。
请注意,根据   技术说明,请求和响应的中所有头字段的名称都是大小写不敏感的(不区分大小写)。
如果服务端的响应符合上述的描述的话,那么就说明 WebSocket 的连接已经建立了,并且连接的状态变为 “OPEN 状态”。另外,服务端的握手响应中也可以包含 cookie 信息,cookie 信息被称为是 “服务端开始握手的 cookie 设置”。
返回列表