ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 关闭连接 ## 7.1.定义 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#711%E5%85%B3%E9%97%ADwebsocket%E8%BF%9E%E6%8E%A5)7.1.1.关闭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()方法。 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#712%E5%90%AF%E5%8A%A8websocket%E5%85%B3%E9%97%AD%E9%98%B6%E6%AE%B5%E6%8F%A1%E6%89%8B)7.1.2.启动WebSocket关闭阶段握手 为了*启动WebSocket关闭阶段握手*,其带有一个状态码(7.4节)/code/和一个可选的关闭原因(7.1.6节)/reason/,一个端点必须按照5.5.1节的描述发送一个Close控制帧,其状态码设置为/code/且其关闭原因设置为/reason/。一旦一个端点已经发送并接收到一个Close控制帧,那个端点应该按照7.1.1节的描述*关闭WebSocket连接*。 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#713-websocket%E5%85%B3%E9%97%AD%E9%98%B6%E6%AE%B5%E6%8F%A1%E6%89%8B%E5%B7%B2%E5%90%AF%E5%8A%A8)7.1.3\. WebSocket关闭阶段握手已启动 一旦发送或接收到一个Close控制帧,这就是说,*WebSocket 关闭阶段握手已启动*,且WebSocket连接处于CLOSING状态。 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#714-websocket%E5%B7%B2%E5%85%B3%E9%97%AD)7.1.4\. WebSocket已关闭 当底层TCP连接已关闭,这就是说*WebSocket连接已关闭*且WebSocket连接处于CLOSED状态。如果TCP连接在WebSocket关闭阶段我是已经完成后被关闭,WebSocket连接被说成已经*完全地*关闭了。 如果WebSocket连接不能被建立,这就是说,*WebSocket连接关闭了*,但不是*完全的*。 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#715websocket%E8%BF%9E%E6%8E%A5%E5%85%B3%E9%97%AD%E4%BB%A3%E7%A0%81)7.1.5.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连接关闭代码*是可能的。 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#716-websocket%E8%BF%9E%E6%8E%A5%E5%85%B3%E9%97%AD%E5%8E%9F%E5%9B%A0)7.1.6\. WebSocket连接关闭原因 按照5.5.1和7.4节的定义,一个控Close控制帧可以包含一个指示关闭原因的状态码,接着是UTF-8编码的数据,上述数据留给断点解释且本协议没有定义。WebSocket连接的关闭可以被任何一个端点初始化,可能同时发生。*WebSocket 连接关闭原因*由跟在包含在实现该协议的应用接收到的第一个Close控制帧状态码(7.4节)后边的UTF-8编码的数据定义。如果Close控制帧中没有这样的数据,*WebSocket连接关闭原因*是空字符串。 注意:按照7.1.5节指出的相同的逻辑,两个端点可以有不一致的*WebSocket连接关闭原因*。 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#717%E5%A4%B1%E8%B4%A5websocket%E8%BF%9E%E6%8E%A5)7.1.7.失败WebSocket连接 某些算法和规范要求端点*失败WebSocket连接*。要做到这一点,客户端必须*关闭WebSocket连接*,并可以以适当的方式把问题报告给用户(这将对开发人员非常有用的)。 同样的,为了做到这一点,服务器必须*关闭WebSocket连接*,并应该记录下问题。 如果*已建立的WebSocket连接*在端点需要*失败WebsSocket连接*之前,端点应该在处理*关闭WebSocket连接*之前发送一个带有适当状态码的Close帧(7.4节)。 如果端点认为另一边不太可能收到并处理关闭帧可以省略发送一个关闭帧,因为错误的性质,导致WebSocket连接失败摆在首要位置。端点必须在被指示为*失败WebSocket端点*之后不继续尝试处理来自远程端点的数据(包括响应关闭帧)。 除上边指出的或由应用层指定的(例如,使用WebSocket API的脚本),客户端应该关闭连接。 ## [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#72%E5%BC%82%E5%B8%B8%E5%85%B3%E9%97%AD)7.2.异常关闭 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#721%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%8F%91%E8%B5%B7%E7%9A%84%E5%85%B3%E9%97%AD)7.2.1.客户端发起的关闭 某些算法,尤其在打开阶段握手期间,需要客户端*失败WebSocket连接*。为了做到这一点,客户端必须按照7.1.7节定义的那样*失败WebSocket连接*。 如果在任何时候,底层的传输层连接意外丢失,客户端必须*失败WebSocket连接*。 除上边指出的或由应用层指定的(例如,使用WebSocket API的脚本),客户端应该关闭连接。 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#722%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%8F%91%E8%B5%B7%E7%9A%84%E5%85%B3%E9%97%AD)7.2.2.服务端发起的关闭 某些算法需要或推荐服务端在打开阶段握手期间*中断WebSocket连接*。为了做到这一点,服务端必须简单地*关闭WebSocket连接*(7.1.1节)。 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#723%E4%BB%8E%E5%BC%82%E5%B8%B8%E5%85%B3%E9%97%AD%E4%B8%AD%E6%81%A2%E5%A4%8D)7.2.3.从异常关闭中恢复 异常关闭可能由任何原因引起。这样的关闭可能是一个瞬时错误导致的,在这种情况下重新连接可能导致一个好的连接和一个重新开始的正常操作。这样的关闭也可能是一个非瞬时问题的导致的,在这种情况下如果每个部署的客户端遇到异常关闭并立即且持续地的尝试重新连接,服务端可能会因为大量的客户端尝试重新连接遇到的拒绝服务攻击。这种情况的最终结果可能是服务不能及时的恢复或恢复是更加困难。 为了避免这个,当客户端遇到本节描述的异常关闭之后尝试重新连接时,应该使用某种形式的补偿。 第一个重新连接尝试应该延迟一个随机的时间量。这种随机延迟的参数的选择留给客户端决定;一个可随机选择的的值在0到5秒是一个合理的初始延迟,不过客户端可以选择不同的间隔由于其选择一个延迟长度基于实现经验和特定的应用。 第一次重新连接尝试失败,随后的重新连接尝试应该延迟递增的时间量,使用的方法如截断二进制指数退避算法。 ## [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#73%E6%AD%A3%E5%B8%B8%E8%BF%9E%E6%8E%A5%E5%85%B3%E9%97%AD)7.3.正常连接关闭 服务端在需要时可能关闭WebSocket连接。客户端不能随意关闭WebSocket连接。在这两种情况下,端点通过如下过程*开始WebSocket关闭握手*初始化一个关闭(7.1.2节)。 ## [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#74%E7%8A%B6%E6%80%81%E7%A0%81)7.4.状态码 当关闭一个已经建立的连接(例如,当在打开阶段握手已经完成后发送一个关闭帧),端点可以表明关闭的原因。由端点解释这个原因,并且端点应该给这个原因采取动作,本规范是没有定义的。本规范定义了一组预定义的状态码,并指定哪些范围可以被扩展、框架和最终应用使用。状态码和任何相关的文本消息是关闭帧的可选的组件。 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#741%E5%AE%9A%E4%B9%89%E7%9A%84%E7%8A%B6%E6%80%81%E7%A0%81)7.4.1.定义的状态码 当发送关闭帧时端点可以使用如下预定义的状态码。 * 1000 1000表示正常关闭,意思是建议的连接已经完成了。 * 1001 1001表示端点“离开”(going away),例如服务器关闭或浏览器导航到其他页面。 * 1002 1002表示端点因为协议错误而终止连接。 * 1003 1003表示端点由于它收到了不能接收的数据类型(例如,端点仅理解文本数据,但接收到了二进制消息)而终止连接。 * 1004 保留。可能在将来定义其具体的含义。 * 1005 1005是一个保留值,且不能由端点在关闭控制帧中设置此状态码。它被指定用在期待一个用于表示没有状态码是实际存在的状态码的应用中。 * 1006 1006是一个保留值,且不能由端点在关闭控制帧中设置此状态码。它被指定用在期待一个用于表示连接异常关闭的状态码的应用中。 * 1007 1007表示端点因为消息中接收到的数据是不符合消息类型而终止连接(比如,文本消息中存在非UTF-8[[RFC3629](http://www.faqs.org/rfcs/rfc3629.html)]数据)。 * 1008 1008表示端点因为接收到的消息违反其策略而终止连接。这是一个当没有其他合适状态码(例如1003或1009)或如果需要隐藏策略的具体细节时能被返回的通用状态码。 * 1009 1009表示端点因接收到的消息对它的处理来说太大而终止连接。 * 1010 1010表示端点(客户端)因为它期望服务器协商一个或多个扩展,但服务器没有在WebSocket握手响应消息中返回它们而终止连接。 所需要的扩展列表应该出现在关闭帧的/reason/部分。 注意,这个状态码不能被服务器端使用,因为它可以失败WebSocket握手。 * 1011 1011表示服务器端因为遇到了一个不期望的情况使它无法满足请求而终止连接。 * 1015 1015是一个保留值,且不能由端点在关闭帧中被设置为状态码。它被指定用在期待一个用于表示连接由于执行TLS握手失败而关闭的状态码的应用中(比如,服务器证书不能验证)。 ### [](https://github.com/zhangkaitao/websocket-protocol/wiki/7.%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5#742%E4%BF%9D%E7%95%99%E7%9A%84%E7%8A%B6%E6%80%81%E7%A0%81%E8%8C%83%E5%9B%B4)7.4.2.保留的状态码范围 * 0-999 0-999范围内的状态码不被使用。 * 1000-2999 1000-2999范围内的状态码保留给本协议、其未来的修订和一个永久的和现成的公共规范中指定的扩展的定义。 * 3000-3999 3000-3999范围内的状态码保留给库、框架和应用使用。这些状态码直接向IANA注册。本规范未定义这些状态码的解释。 * 4000-4999 4000-4999范围内的状态码保留用于私有使用且因此不能被注册。这些状态码可以被在WebSocket应用之间的先前的协议使用。本规范未定义这些状态码的解释。