7.关闭连接——WebSocket协议翻译
为_关闭WebSocket连接_,端点需关闭底层TCP连接。端点应该使用一个方法完全地关闭TCP连接,以及TLS会话,如果合适,丢弃任何可能已经接收的尾随的字节。当必要时端点可以通过任何可用的手段关闭连接,例如当受到攻击时。
底层TCP连接,在大多数正常情况下,应该首先被服务器关闭,所以它持有TIME_WAIT状态而不是客户端(因为这会防止它在2个报文最大生存时间(2MLS)内重新打开连接,然而当一个新的带有更高的seq number的SYN时没有对应的服务器影响TIME_WAIT连接被立即重新打开)。在异常情况下(例如在一个合理的时间量后没有接收到服务器的TCP Close)客户端可以发起TCP Close。因此,当服务器被指示_关闭WebSocket连接_,它应该立即发起一个TCP Close,且当客户端被知识也这么做时,它应该等待服务器的一个TCP Close。
例如一个如何使用Berkeley socket在C中得到完全地关闭的例子,一端会在socket上以SHUT_WR调用shutdown(),调用recv()直到获得一个指示那个节点也已经执行了一个有序关闭的0返回值,且最终在socket上调用close()方法。
为了_启动WebSocket关闭阶段握手_,其带有一个状态码(7.4节)/code/和一个可选的关闭原因(7.1.6节)/reason/,一个端点必须按照5.5.1节的描述发送一个Close控制帧,其状态码设置为/code/且其关闭原因设置为/reason/。一旦一个端点已经发送并接收到一个Close控制帧,那个端点应该按照7.1.1节的描述_关闭WebSocket连接_。
一旦发送或接收到一个Close控制帧,这就是说,_WebSocket 关闭阶段握手已启动_,且WebSocket连接处于CLOSING状态。
当底层TCP连接已关闭,这就是说_WebSocket连接已关闭_且WebSocket连接处于CLOSED状态。如果TCP连接在WebSocket关闭阶段我是已经完成后被关闭,WebSocket连接被说成已经_完全地_关闭了。 如果WebSocket连接不能被建立,这就是说,_WebSocket连接关闭了_,但不是_完全的_。
按照5.5.1和7.4节的定义,一个Close控制帧可以包含一个表示关闭原因的状态码。一个正关闭的WebSocket连接可以同时由两个端点初始化。_WebSocket连接Close Code_定义为包含在由实现该协议的应用接收到的第一个Close控制帧的状态码(7.4节)。如果这个Close控制帧不包含状态码,_WebSocket连接Close Code_被认为是1005。如果_WebSocket连接已经关闭_且端点没有接收到Close状态码(例如可能发生在底层传输连接丢失时),_WebSocket连接Close Code_被认为是1006。
注意:两个端点可以有不一致的_WebSocket连接关闭代码_。例如,如果远程端点发送了一个Close帧,但本地应用还没有从它的socket接收缓冲区中读到包含Close帧的数据,且本地应用独立地决定关闭连接和发送一个Close帧,两个端点都将发送和接收Close帧且将不发送更多的Close帧。每一个端点将看见另一端发送的以_WebSocket连接关闭代码_结束的状态码。例如,在两个端点独立且在大致相同的时间同时_开启WebSocket关闭阶段握手_的情况下,两个端点可以有不一致的_WebSocket连接关闭代码_是可能的。
按照5.5.1和7.4节的定义,一个控Close控制帧可以包含一个指示关闭原因的状态码,接着是UTF-8编码的数据,上述数据留给断点解释且本协议没有定义。WebSocket连接的关闭可以被任何一个端点初始化,可能同时发生。_WebSocket 连接关闭原因_由跟在包含在实现该协议的应用接收到的第一个Close控制帧状态码(7.4节)后边的UTF-8编码的数据定义。如果Close控制帧中没有这样的数据,_WebSocket连接关闭原因_是空字符串。
注意:按照7.1.5节指出的相同的逻辑,两个端点可以有不一致的_WebSocket连接关闭原因_。
某些算法和规范要求端点_失败WebSocket连接_。要做到这一点,客户端必须_关闭WebSocket连接_,并可以以适当的方式把问题报告给用户(这将对开发人员非常有用的)。
同样的,为了做到这一点,服务器必须_关闭WebSocket连接_,并应该记录下问题。
如果_已建立的WebSocket连接_在端点需要_失败WebsSocket连接_之前,端点应该在处理_关闭WebSocket连接_之前发送一个带有适当状态码的Close帧(7.4节)。
如果端点认为另一边不太可能收到并处理关闭帧可以省略发送一个关闭帧,因为错误的性质,导致WebSocket连接失败摆在首要位置。端点必须在被指示为_失败WebSocket端点_之后不继续尝试处理来自远程端点的数据(包括响应关闭帧)。
除上边指出的或由应用层指定的(例如,使用WebSocket API的脚本),客户端应该关闭连接。
某些算法,尤其在打开阶段握手期间,需要客户端_失败WebSocket连接_。为了做到这一点,客户端必须按照7.1.7节定义的那样_失败WebSocket连接_。
如果在任何时候,底层的传输层连接意外丢失,客户端必须_失败WebSocket连接_。 除上边指出的或由应用层指定的(例如,使用WebSocket API的脚本),客户端应该关闭连接。
某些算法需要或推荐服务端在打开阶段握手期间_中断WebSocket连接_。为了做到这一点,服务端必须简单地_关闭WebSocket连接_(7.1.1节)。
异常关闭可能由任何原因引起。这样的关闭可能是一个瞬时错误导致的,在这种情况下重新连接可能导致一个好的连接和一个重新开始的正常操作。这样的关闭也可能是一个非瞬时问题的导致的,在这种情况下如果每个部署的客户端遇到异常关闭并立即且持续地的尝试重新连接,服务端可能会因为大量的客户端尝试重新连接遇到的拒绝服务攻击。这种情况的最终结果可能是服务不能及时的恢复或恢复是更加困难。
为了避免这个,当客户端遇到本节描述的异常关闭之后尝试重新连接时,应该使用某种形式的补偿。
第一个重新连接尝试应该延迟一个随机的时间量。这种随机延迟的参数的选择留给客户端决定;一个可随机选择的的值在0到5秒是一个合理的初始延迟,不过客户端可以选择不同的间隔由于其选择一个延迟长度基于实现经验和特定的应用。
第一次重新连接尝试失败,随后的重新连接尝试应该延迟递增的时间量,使用的方法如截断二进制指数退避算法。
服务端在需要时可能关闭WebSocket连接。客户端不能随意关闭WebSocket连接。在这两种情况下,端点通过如下过程_开始WebSocket关闭握手_初始化一个关闭(7.1.2节)。
当关闭一个已经建立的连接(例如,当在打开阶段握手已经完成后发送一个关闭帧),端点可以表明关闭的原因。由端点解释这个原因,并且端点应该给这个原因采取动作,本规范是没有定义的。本规范定义了一组预定义的状态码,并指定哪些范围可以被扩展、框架和最终应用使用。状态码和任何相关的文本消息是关闭帧的可选的组件。
当发送关闭帧时端点可以使用如下预定义的状态码。
1000
1000表示正常关闭,意思是建议的连接已经完成了。
1001
1001表示端点“离开”(going away),例如服务器关闭或浏览器导航到其他页面。
1002
1002表示端点因为协议错误而终止连接。
1003
1003表示端点由于它收到了不能接收的数据类型(例如,端点仅理解文本数据,但接收到了二进制消息)而终止连接。
1004 保留。可能在将来定义其具体的含义。
1005
1005是一个保留值,且不能由端点在关闭控制帧中设置此状态码。它被指定用在期待一个用于表示没有状态码是实际存在的状态码的应用中。
1006
1006是一个保留值,且不能由端点在关闭控制帧中设置此状态码。它被指定用在期待一个用于表示连接异常关闭的状态码的应用中。
1007
1007表示端点因为消息中接收到的数据是不符合消息类型而终止连接(比如,文本消息中存在非UTF-8[RFC3629]数据)。
1008
1008表示端点因为接收到的消息违反其策略而终止连接。这是一个当没有其他合适状态码(例如1003或1009)或如果需要隐藏策略的具体细节时能被返回的通用状态码。
1009
1009表示端点因接收到的消息对它的处理来说太大而终止连接。
1010
1010表示端点(客户端)因为它期望服务器协商一个或多个扩展,但服务器没有在WebSocket握手响应消息中返回它们而终止连接。 所需要的扩展列表应该出现在关闭帧的/reason/部分。
注意,这个状态码不能被服务器端使用,因为它可以失败WebSocket握手。
1011
1011表示服务器端因为遇到了一个不期望的情况使它无法满足请求而终止连接。
1015
1015是一个保留值,且不能由端点在关闭帧中被设置为状态码。它被指定用在期待一个用于表示连接由于执行TLS握手失败而关闭的状态码的应用中(比如,服务器证书不能验证)。
0-999
0-999范围内的状态码不被使用。
1000-2999
1000-2999范围内的状态码保留给本协议、其未来的修订和一个永久的和现成的公共规范中指定的扩展的定义。
3000-3999
3000-3999范围内的状态码保留给库、框架和应用使用。这些状态码直接向IANA注册。本规范未定义这些状态码的解释。
4000-4999
4000-4999范围内的状态码保留用于私有使用且因此不能被注册。这些状态码可以被在WebSocket应用之间的先前的协议使用。本规范未定义这些状态码的解释。