【译】 WebSocket 协议第七章——关闭链接(Closing the Connection)

概述

本文为 WebSocket 协议的第七章,本文翻译的主要内容为 WebSocket 链接关闭相关内容。算法

关闭链接(协议正文)

7.1 定义

7.1.1 关闭 WebSocket 链接

关闭 WebSocket 链接,终端须要关闭底层的 TCP 链接。终端须要使用一个方法来干净的关闭TCP链接,还有 TLS 会话,若是可能的话,抛弃后面可能受到的任意字符。终端可能会在须要的时候,经过任何方式来关闭链接,例如在收到攻击时。浏览器

在底层的 TCP 链接中,一般大多数状况下,服务端应该先关闭,因此是服务端而不是客户端保持 TIME_WAIT 状态(由于客户端先关闭的话,这会阻止服务端在2 MSL 内从新打开这条链接,而若是服务器处于 TIME_WAIT 状态下,若是收到了一个带有更大序列号的新的 SYN 包时,也可以当即响应从新打开链接,从而不会对服务器产生影响)。反常状况(例如在合理的时间后,服务端收到一个 TCP 关闭包)下,客户端应该开始关闭 TCP 链接。像这样的,当服务端进入关闭 WebSocket 链接状态时,它应该马上准备关闭 TCP 链接,而后当客户端客户端准备关闭链接时,他应该等待服务端的 TCP 关闭包。服务器

用 C 语言的 Berkeley socket 做为例子来展现如何完全的关闭链接,一端须要用 SHUP_WR 调用 shutdown() 方法,调用 recv() 直到得到一个值为 0 的表示对面也准备有序关闭链接的返回值,而后最后调用 close() 来关闭 socket 通道。闭包

7.1.2 开始进行 WebSocket 关闭握手

用一个状态码 code (第 7.4 节)和一个可选的关闭缘由 reason (第 7.1.6 节)来开始 WebSocket 关闭握手,终端必须发送一个在第 5.5.1 节中描述的同样的关闭帧,将状态码设置为 code 字段,将关闭缘由设置为 reaons 字段。一旦终端已经发送和收到了关闭控制帧,那么终端应该像第 7.1.1 节中定义的同样关闭 WebSocket 链接框架

7.1.3 已经开始 WebSocket 关闭握手

在发送或者收到了关闭帧时,咱们能够说已经开始 WebSocket 关闭握手,而且 WebSocket 链接的状态已经到了“关闭中”(CLOSING)状态。socket

7.1.4 WebSocket 链接已关闭

当底层的 TCP 链接关闭后,咱们能够说WebSocket 链接已关闭,而且 WebSocket 链接已经到了”关闭“(CLOSED)状态。若是 TCP 链接在 WebSocket 关闭握手完成以后已经关闭,那么咱们能够说 WebSocket 链接已经被完全关闭。编码

若是 WebSocket 链接没有被创建,咱们也说WebSocket已经关闭,可是不完全翻译

7.1.5 WebSocket 关闭状态码

就像在第 5.5.1 和第 7.4 节中定义的同样,关闭帧能够包含一个关闭的状态码和指定的缘由。WebSocket 链接的关闭多是同时由另外一个终端发起。WebSocket 关闭状态码是在第 7.4 节中定义的在第一关闭帧中的由实现该协议的应用程序接收的状态码。若是关闭帧中没有包含状态码,WebSocket 关闭状态码被默认为1005。若是WebSocket 已经关闭而且终端没有收到任何的关闭帧(例如发生了可能底层的传输链接忽然丢失的状况),那么WebSocket 关闭状态码被默认为1006。设计

注:两个终端可能没有就WebSocket 关闭状态码的值达成一致。例如:若是远端发送一个关闭帧,可是本地应用没有从它的 socket 缓冲区中读到关闭帧的数据,同时本地应用单独的决定关闭链接而且发送了一个关闭帧,那么两个终端都发送了而且会收到一个关闭帧,同时不会发送更多的关闭帧。每个终端会看到另外一个终端发送过来的WebSocket 关闭状态码的状态码。像这样的,在这个示例里面,有可能两个终端都没有协商过WebSocket 关闭状态码,两个终端都几乎在同一时间单独开始 WebSocket 关闭握手日志

7.1.6 WebSocket 链接关闭缘由

像第 5.5.1 节和第 7.4 节中定义的同样,一个关闭帧可能包含一个用于关闭的表示缘由的状态码,而后是 UTF-8 编码的数据,数据的解析方式是留给终端来解释,而不在这个协议中定义。一个正在关闭中的 WebSocket 链接多是同时从另外一端开始的。WebSocket 链接关闭缘由是实现了该协议的应用收到的紧跟在状态码(第 7.4 节)以后的包含在第一个关闭控制帧中的 UTF-8 编码数据。若是在关闭控制帧中没有这些数据,那么WebSocket 链接关闭缘由的值就是一个空字符串。

注:和在第 7.1.5 中被提到的逻辑同样,两个终端可能没有协商过WebSocket 链接关闭缘由

7.1.7 WebSocket 链接失效

某些算法和规范要求终端有WebSocket 链接失效。为了实现这些,客户端必须关闭 WebSocket 链接,而且能够用一个合适的方式向用户上报相关问题(尤为是对开发者有帮助的内容)。类似的,为了实现这个,服务端必须关闭 WebSocket 链接,而且应该用日志记录这个问题。

若是在此以前WebSocket 已经创建链接,此时终端须要让WebSocket 链接失效,那么在进行关闭 WebSocket 链接以前,终端须要发送一个包含恰当的状态码(第 7.4 节)。终端在确认另外一端没有能力接收或者处理关闭帧时,可能会选择省略发送关闭帧,从而在一开始就进入正常错误流程致使 WebSocket 链接关闭。终端在接到WebSocket 链接失效的指令后,不能继续尝试处理来自另外一端的数据(包括响应的关闭帧)。

除了上面说到的场景和应用层指定的场景(例如:脚本使用了 WebSocket 的 API)外,客户端不该该关闭链接。

7.2 异常关闭

7.2.1 客户端主动关闭

在开始握手中的某些特定算法,须要客户端让WebSocket 链接失效。为了实现这些,客户端必须像第 7.1.7 节中定义的同样让WebSocket 链接失败。

若是任意一端底层的传输链接意外丢失,客户端必须让WebSocket 链接失败

除了上面指定的状况和应用层的约束(例如,脚本使用了 WebSocket 的 API)外,客户端不该该关闭链接。

7.2.2 服务端主动关闭

在开始监创建链接握手时,有些算法要求或者推荐服务端终端 WebSocket 链接。为了实现这些,服务端必须关闭 WebSocket 链接(第 7.1.1 节)。

7.2.3 从异常关闭中恢复

致使异常关闭的缘由有不少。例如是因为一个临时的错误致使的关闭,在这种状况下可以恢复就可以带来一个稳定的链接,恢复正常的操做。有些问题也有多是一个非临时的问题致使的,在这种状况下若是每一个客户端都遇到了异常的关闭,客户端马上重试链接而且不间断状况下,服务端可能会收到因为大量客户端从新链接带来的拒绝服务攻击。最终的结果就是这个方案可能会致使服务没有办法及时的恢复,或者让服务恢复变得困难的多。

为了不这个问题,客户端应该在异常终端尝试恢复链接时,使用在这一节中定义的一些备选策略。

第一次尝试恢复链接应该在一个随机长度时间后。随机事件的参数如何选择,这个交给客户端来决定;选择 0 到 5 秒之间的随机值是一个合理的初始延时,可是客户端能够根据本身的经验和特定的应用来选择不一样长度的时间延时。

若是第一次重试链接失败,接下来的链接的延时应该变大,使用如截断二进制指数退避方法(译者注:解决以太网碰撞算法,见截断二进制质数退避算法)等来进行设置这个延时。

7.3 链接正常关闭

服务端能够在任意须要时关闭 WebSocket 链接。客户端不该该任意关闭 WebSocket 链接。在任一状况中,终端要发起关闭都必须遵循开始 WebSocket 链接关闭的步骤。

7.4 状态码

当关闭一个链接时(如:在开始握手已经完成后,发送一个关闭帧),终端可能会说明关闭的缘由。终端的这个缘由的描述和终端应该采起的行动,在这个文档中都没有说明。这个文档提早定义了一些可能用于扩展、框架和终端应用的状态码和状态码范围。这些状态码和任何有关联的的文本消息在关闭帧中都是可选的。

7.4.1 定义状态码

在发送一个关闭帧时,终端能够提早定义以下的状态码。

1000

1000 表示一个正常的关闭,意味着链接创建的目标已经完成了。

1001

1001 表示终端已经“走开”,例如服务器停机了或者在浏览器中离开了这个页面。

1002

1002 表示终端因为协议错误停止了链接。

1003

1003 表示终端因为收到了一个不支持的数据类型的数据(如终端只能怪理解文本数据,可是收到了一个二进制数据)从而关闭链接。

1004

保留字段。这意味着这个状态码可能会在未来被定义。

1005

1005 是一个保留值而且不能被终端当作一个关闭帧的状态码。这个状态码是为了给上层应用表示当前没有状态码。

1006

1006 是一个保留值而且不能被终端当作一个关闭帧的状态码。这个状态码是为了给上层应用表示链接被异常关闭如没有发送或者接受一个关闭帧这种场景的使用而设计的。

1007

1007 表示终端由于收到了类型不连续的消息(如非 UTF-8 编码的文本消息)致使的链接关闭。

1008

1008 表示终端是由于收到了一个违反政策的消息致使的链接关闭。这是一个通用的状态码,能够在没有什么合适的状态码(如 1003 或者 1009)时或者可能须要隐藏关于政策的具体信息时返回。

1009

1009 表示终端因为收到了一个太大的消息没法进行处理从而关闭链接。

1010

1010 表示终端(客户端)由于预期与服务端协商一个或者多个扩展,可是服务端在 WebSocket 握手中没有响应这个致使的关闭。须要的扩展清单应该出如今关闭帧的缘由(reason)字段中。

1001

1001 表示服务端由于遇到了一个意外的条件阻止它完成这个请求从而致使链接关闭。

1015

1015 是一个保留值,不能被终端设置到关闭帧的状态码中。这个状态码是用于上层应用来表示链接失败是由于 TLS 握手失败(如服务端证书没有被验证过)致使的关闭的。

7.4.2 保留状态码范围

0-999

0-999 的状态码都没有被使用。

1000-2999

1000-2999 的状态码是在这个文档、未来的修订和扩展中定义的保留字段,用于永久的可用的公共文档。

3000-3999

3000-3999 的状态码是保留给库、框架和应用使用的。这些状态码被IANA直接注册了。这些状态码在这篇文档中没有进行解释。

4000-4999

40000-4999 的状态码是保留下来私用的,所以这些状态码不能被注册。这些状态码可使用在 WebSocket 应用以前的协议上。这些状态码在这篇文档中没有进行解释。