Javascript 不会在 WebSocket 实例化中捕获错误

Posted

技术标签:

【中文标题】Javascript 不会在 WebSocket 实例化中捕获错误【英文标题】:Javascript doesn't catch error in WebSocket instantiation 【发布时间】:2015-06-23 12:17:38 【问题描述】:

我的套接字当前抛出 net::ERR_CONNECTION_REFUSED 因为服务器没有运行,我希望它现在这样做。

问题是下面的代码没有捕捉到错误。在控制台中,我在第 2 行(使用 net::ERR_CONNECTION_REFUSED)看到了一个异常,我认为它不应该发生,因为它在 try 语句中。

1  try 
2    ws = new WebSocket('ws://'+ host + ':' + port + '/');
3  
4  catch (err) 
5    console.log('This never prints');
6  
7  ws.onerror = function (error) 
8    console.log(error);
9  ;

所以我的问题是为什么它没有被抓住?

我最终想要的是在其他地方显示的错误消息,但我无法捕捉到它,第 8 行打印了一个“事件”对象,它没有提到 net::ERR_CONNECTION_REFUSED,所以我不确定如何以获取错误消息。

【问题讨论】:

好问题。同样的问题。 【参考方案1】:

WebSocket 的连接时间错误导致调度事件,而不是抛出值。这是因为throw 操作必须是同步的。为了将所有连接时错误作为抛出错误处理,WebSocket 构造函数需要完全暂停所有脚本执行和 UI 交互,直到整个 WebSocket 握手完成。相反,连接过程异步运行,从而允许浏览器线程在 WebSocket 连接在后台初始化时继续工作。由于连接的异步特性,WebSocket 必须通过error 事件报告错误,因为在异步连接任务遇到错误时同步的new WebSocket 操作已经完成。

您看到的ERR_CONNECTION_REFUSED 消息纯粹是为了开发者的利益;脚本无法以任何方式访问它。它在 javascript 环境中没有任何表示。它只是显示在您的控制台中的一条红色消息,用于通知 (查看浏览器的人)出现错误。

error 处理程序事件是响应失败的正确位置,但缺少脚本可读的连接时间错误信息是设计使然。来自WHATWG spec for the WebSocket API:

用户代理不得以允许脚本区分以下情况的方式向脚本传达任何失败信息:

无法解析主机名的服务器。 数据包无法成功路由到的服务器。 服务器拒绝指定端口上的连接。 服务器未能正确执行 TLS 握手(例如,无法验证服务器证书)。 未完成打开握手的服务器(例如,因为它不是 WebSocket 服务器)。 WebSocket 服务器发送了正确的打开握手,但指定了导致客户端断开连接的选项(例如,服务器指定了客户端未提供的子协议)。 在成功完成打开握手后突然关闭连接的 WebSocket 服务器。

[...] 允许脚本区分这些情况将允许脚本探测用户的本地网络以准备攻击。

浏览器故意忽略规范要求的任何有用信息。规范作者担心访问此信息可能会让恶意网页获取有关您的网络的信息,因此他们要求浏览器以无法区分的方式报告所有连接时间错误。

【讨论】:

这是非常有用的信息,尽管我不明白为什么没有“捕获”异常。这似乎打破了语言的基本框架。我认为做我想做的事情的方法是处理 onclose 并查看 CloseEvent 的代码。 @AndyHasIt 我为此添加了一个开头段落;我希望它足够清楚。基本上,我们不能catch 出错,因为我们在异步连接尝试遇到错误之前同步退出了try 块。 我怎样才能防止错误出现在控制台中? ws.onerror = ev => ev.stopImmediatePropagation() ev.stopPropagation() ev.preventDefault() return false 都没有帮助。 @OuuGiii 您可以使用 Promise catch 方法,但 WebSocket API 不支持 Promise,因此您需要创建自己的 Promise 对象.见this fiddle I just made。你仍然需要监听错误事件并调用reject 来触发任何catch 机制。你甚至不能throw 因为onerror 分辨率是在new Promise 函数之外按时间顺序发生的。同样,WebSocket 构造函数在连接失败时所做的所有事情都是调度一个恰好名为 error 的事件;它不会引发异常。 这不好。我的 Web 客户端每 1 秒轮询一次未运行的服务器,并且每次都写入控制台记录此错误。【参考方案2】:

我尝试用它来制作小提琴 我可以看到打印的行。

host='localhost';
port=100;
try 

    ws = new WebSocket('ws://'+ host + ':' + port + '/');
  
  catch (err) 
    console.log('This never prints');
  
  ws.onerror = function (error) 
    console.log(error);
  ;

https://jsfiddle.net/xzumgag0/

【讨论】:

这是由于另一个错误:“可能无法从通过 HTTPS 加载的页面启动不安全的 WebSocket 连接。” 确实,jsfiddle 示例捕获了错误,但如果您打开控制台并运行它,它不会捕获错误。正如 dfreeman 指出的那样,这些是不同的错误。

以上是关于Javascript 不会在 WebSocket 实例化中捕获错误的主要内容,如果未能解决你的问题,请参考以下文章

使用 nginx 作为 websocket 连接的代理

使用 SSL 创建 PHP WebSocket

Javascript - 在 Websocket 上使用承诺?

我将如何在 Javascript 中通过 WebSocket 发送和接收数据包

Websocket 连接不会在 FireFox 中关闭

如何在 Javascript 中为 websocket 的请求标头设置 cookie?