获取Websockets关闭的原因,关闭代码为1006。

138

我想获取websockets关闭的原因,这样我就可以向用户显示正确的消息。

我有:

sok.onerror=function (evt) 
     {//since there is an error, sockets will close so...
       sok.onclose=function(e){
           console.log("WebSocket Error: " , e);}

代码始终为1006,原因始终是“ ”。但我想要区分不同的关闭原因。

例如,命令行会给出错误原因:“您无法删除它,因为数据库不允许”。但在Chrome的控制台上,原因仍然是“ ”。

有其他方法可以区分不同的关闭原因吗?


我认为这是由于服务器处理连接/断开事件的方式。我不能确定,但是连接关闭也需要在服务器上正确处理代码。尝试覆盖服务器上内置的On Connected / Disconnected方法并查看。我的假设只是你正在关闭它,但服务器没有正确关闭,因此无法传递正确的关闭响应。 - Michael Puckett II
7个回答

185

关闭代码1006是一个特殊的代码,意味着浏览器实现本地异常关闭了连接。

如果您的浏览器客户端报告关闭代码1006,那么您应该查看websocket.onerror(evt)事件以获取详细信息。

然而,Chrome很少向Javascript方报告任何关闭代码1006的原因。这可能是由于WebSocket规范中的客户端安全规则,以防止滥用WebSocket(例如使用它扫描目标服务器上的开放端口,或生成大量连接进行拒绝服务攻击)。

请注意,如果在HTTP升级到WebSocket期间发生错误(这是WebSocket技术上“连接”之前的步骤),Chrome通常会报告关闭代码1006。出现此类问题的原因可能包括身份验证或授权失败,或使用错误的协议(例如请求子协议,但服务器本身不支持该子协议),甚至尝试与不是WebSocket(例如尝试连接到ws://images.google.com/)的服务器位置进行通信。

基本上,如果您看到关闭代码1006,则WebSocket本身存在非常低级的错误(类似于“无法打开文件”或“套接字错误”),并不适用于用户,因为它指向您的代码和实现中的低级问题。修复您的低级问题,然后当您连接时,您可以包括更合理的错误代码。您可以在项目范围或严重程度方面实现此目标。例如:信息和警告级别是您项目特定协议的一部分,并且不会导致连接终止。对于严重或致命消息,还可以使用您的项目协议来传达尽可能多的详细信息,然后使用WebSocket关闭流的有限能力关闭连接。

需要注意的是WebSocket关闭代码非常严格定义,关闭原因短语/消息长度不能超过123个字符(这是WebSocket有意限制的)。

但并非一切都失去了,如果您只是想获取此信息进行调试,Chrome的Javascript控制台通常会报告关闭的详细信息及其底层原因。


5
Joakim,谢谢你详细的回答。如果我使用sok.onerror=function (evt) {console.log(evt);},细节就不那么多了。甚至没有“reason”或其他东西。那么,根本没有选项吗? 我只需要向用户显示“出了些问题,或者没有连接?” 这样并不太友好,如果用户能看到“您无法删除,因为数据库限制”,那就太好了。有什么选项吗?谢谢 - slevin
1
在我的情况下,出现了相同的1006错误,但这是在Chrome、Firefox等浏览器中发生的,而不是在Opera或iOS设备的Safari中发生的。如果有人有想法,请帮帮我,https://dev59.com/jF0a5IYBdhLWcg3wCEsw - Mrug
@JoakimErdfelt,当我(故意)没有得到任何有关问题的信息时,我该如何解决这些“低级问题”? - El Mac
@JoakimErdfelt 没有任何内容:错误:连接断开,出现错误'错误:WebSocket以状态代码1006()关闭。'。 - El Mac
@FrankQ。不,WebSocket API不能直接访问那个。 - Remy Lebeau
显示剩余7条评论

68

在我的和可能是@BIOHAZARD的情况下,问题出在nginx代理超时。默认情况下,在套接字上没有活动的情况下为60秒。

我将其更改为24小时nginx中,问题得到解决。

proxy_read_timeout 86400s;
proxy_send_timeout 86400s;

4
谢谢!这就是导致我遇到 1006 错误的原因。 - Steve Hanov
如果您得到1006,这就是答案。 - Parinda Rajapaksha
1
这个要在哪里设置?安装完nginx后是一个终端命令吗? - Qadir Hussain
@QadirHussain 你需要在nginx配置文件中添加这些行(位于/usr/local/nginx/conf、/etc/nginx或/usr/local/etc/nginx)。我猜你正在使用nginx作为反向代理? - nulldevops
这肯定是代理问题。我已经测试了通过HAPROXY传递我的连接,然后直接连接到后端组件。我甚至可以看到sessionId也不同。通过HAPROXY传递sessionId是完整的UUID,而直接连接则是一个小哈希值,例如ghvvedi3 - Magno C

21

看起来这是Chrome不符合WebSocket标准的情况。 当服务器启动关闭并向客户端发送关闭帧时,Chrome会认为这是一个错误,并将其报告给JS端,代码为1006且没有原因消息。 在我的测试中,Chrome从未响应服务器启动的关闭帧(关闭代码为1000),这表明代码1006可能意味着Chrome正在报告其自己的内部错误。

P.S. Firefox v57.00正确处理此情况,并成功将服务器的原因消息传递到JS端。


我因为重写不当而得到了404错误,而Chrome甚至没有尝试显示它...谢谢你的提示! - RobM

20

我认为这对其他人可能会有用。了解正则表达式很有用,孩子们要好好学习哦。

编辑:把它变成了一个方便易用的函数!

let specificStatusCodeMappings = {
    '1000': 'Normal Closure',
    '1001': 'Going Away',
    '1002': 'Protocol Error',
    '1003': 'Unsupported Data',
    '1004': '(For future)',
    '1005': 'No Status Received',
    '1006': 'Abnormal Closure',
    '1007': 'Invalid frame payload data',
    '1008': 'Policy Violation',
    '1009': 'Message too big',
    '1010': 'Missing Extension',
    '1011': 'Internal Error',
    '1012': 'Service Restart',
    '1013': 'Try Again Later',
    '1014': 'Bad Gateway',
    '1015': 'TLS Handshake'
};

function getStatusCodeString(code) {
    if (code >= 0 && code <= 999) {
        return '(Unused)';
    } else if (code >= 1016) {
        if (code <= 1999) {
            return '(For WebSocket standard)';
        } else if (code <= 2999) {
            return '(For WebSocket extensions)';
        } else if (code <= 3999) {
            return '(For libraries and frameworks)';
        } else if (code <= 4999) {
            return '(For applications)';
        }
    }
    if (typeof(specificStatusCodeMappings[code]) !== 'undefined') {
        return specificStatusCodeMappings[code];
    }
    return '(Unknown)';
}

使用方法:

getStatusCodeString(1006); //'Abnormal Closure'

{
    '0-999': '(Unused)',
    '1016-1999': '(For WebSocket standard)',
    '2000-2999': '(For WebSocket extensions)',
    '3000-3999': '(For libraries and frameworks)',
    '4000-4999': '(For applications)'
}

{
    '1000': 'Normal Closure',
    '1001': 'Going Away',
    '1002': 'Protocol Error',
    '1003': 'Unsupported Data',
    '1004': '(For future)',
    '1005': 'No Status Received',
    '1006': 'Abnormal Closure',
    '1007': 'Invalid frame payload data',
    '1008': 'Policy Violation',
    '1009': 'Message too big',
    '1010': 'Missing Extension',
    '1011': 'Internal Error',
    '1012': 'Service Restart',
    '1013': 'Try Again Later',
    '1014': 'Bad Gateway',
    '1015': 'TLS Handshake'
}

源(经过简洁的小修改):https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes


那么npm包呢? :-) - Piranna
websocket-close-codes npm包含这些代码。 - whistling_marmot
@whistling_marmot 只能单向传输(为了任何合理的效率)。 - Andrew
@Andrew,你可以从单词 - wsCloseCodes ['PolicyViolation'] - 获取数字,或者从数字 wsCloseCodes [1008] 获取单词。 - whistling_marmot
@whistling_marmot 噢,这是TypeScript... 我把它看作一个对象,因为我以为我们在处理原始JS... - Andrew

8
我在使用Chrome作为客户端、golang gorilla websocket作为服务器,通过nginx代理时遇到了错误。
发送一些“ping”信息从服务器到客户端,每x秒一次,可以解决问题。
更新:哦,我在这个回答之后实现了数十个基于websocket的应用程序,每5秒钟从客户端发送PING是保持与服务器连接活动的正确方法(当我推荐从服务器ping时,我不知道自己在想什么)。

4
我们遇到了同样的问题,实际上 AWS 是我们的问题所在。
设置:WebSocket 连接 -> AWS EC2 负载均衡器 -> Nginx 代理 -> Node.js 后端。
我们在 Nginx 的配置文件中根据 this 的答案增加了超时时间,但并没有看到任何改善。我们现在发现 AWS 负载均衡器默认有一个超时时间为 60 秒。您可以在 EC2 -> 负载均衡器下编辑它 enter image description here
请注意,我们没有实现 ping-pong 机制。我们认为实施这种机制也可以解决问题,但在此之前我们只能使用这个变通方法,并增加空闲超时时间。

1

本文提供的是导致问题的可能原因,而非问题的答案。

我们遇到的问题只影响基于Chromium的浏览器。

我们使用了一台负载均衡器,但浏览器在握手期间发送的字节数超出了协商的范围,导致负载均衡器终止了连接。

TCP窗口缩放技术解决了我们的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接