关闭

前端WebSocket知识点总结

时间: 2020-10-09阅读: 60标签: WebSocket

最近研究了下WebSocket,总结下目前对WebSocket的认知。本文不是基于WebSocket展开的一个从0到1的详细介绍。如果你从来没有了解过WebScoket,建议可以先搜一些介绍WebSocket的文章,这类文章还是挺多的,我就不再赘述了。

下面的内容是基于你对WebSocket有基本了解后展开的几个小的知识点:

  1. ping/pong协议;
  2. 如何使ERROR_INTERNET_DISCONNECTED错误信息不显示在控制台;

ping/pong协议

背景:连接WebSocket的时候,发现WebSocket刚连接上没过多久就断开了,为了保持长时间的连接,就想到了ping/pong协议。

问题:

  1. ping/pong是一种特殊的帧类型吗,还是说只是一种设计思想?
  2. js有原生方法支持发送ping/pong消息吗

通过WebSocket协议,发现ping/pong确实是一种特殊的帧类型:

The Ping frame contains an opcode of 0x9.
The Pong frame contains an opcode of 0xA.

那么,上面所说的opcode又是什么东西呢?讲opcode就得说到帧数据格式:


通过上图可以发现,除了最后面的Payload Data,也就是我们要发送的数据之外,还会有一些其他信息。我觉得可以类比http请求的请求头部分。上图中第5-8位表示的就是opcode的内容。其余字段的含义可以参考上述WebSocket规范,或者搜WebSocket协议数据帧格式,这类博客还是挺多的。

拿nodejs举个例子:
浏览器端发起WebSocket的时候,会发送一个http请求,注意请求头里面的Upgrade字段,意思就是我要升级到websocket连接:

GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

此时,nodeJS就可以监听upgrade事件,去做拒绝或者升级操作,注意下这个事件里面有个参数socket:

socket: <stream.Duplex> Network socket between the server and client

socket有一个write方法,该方法是可以用来写帧数据的,也就是上面帧格式里面的全部数据,而不仅仅是Payload Data。

ws仓库就是使用了socket的write方法发送了根据WebSocket协议定义的ping/pong,部分关键代码如下:

doPing(data, mask, readOnly, cb) {
  this.sendFrame(
    Sender.frame(data, {
      fin: true,
      rsv1: false,
      opcode: 0x09, // ping opcode
      mask,
      readOnly
    }),
    cb
  );
}
doPong(data, mask, readOnly, cb) {
  this.sendFrame(
    Sender.frame(data, {
      fin: true,
      rsv1: false,
      opcode: 0x0a, // pong opcode
      mask,
      readOnly
    }),
    cb
  );
}
sendFrame(list, cb) {
  if (list.length === 2) {
    this._socket.cork();
    this._socket.write(list[0]);
    this._socket.write(list[1], cb);
    this._socket.uncork();
  } else {
    this._socket.write(list[0], cb);
  }
}

所以,nodeJS是可以实现WebSocket协议定义的ping/pong帧的。原因是我们可以拿到socket对象,并且该对象提供了可以发送完整帧数据的方法。那么浏览器端呢?

浏览器提供了原生的WebSocket构造函数用来创建一个WebSocket实例,该实例只提供了一个send方法,并且该send方法只能用来发送上述协议中Payload Data的内容,浏览器会根据send的参数自动生成一个完整的帧数据。所以,在浏览器端是没法控制除了Payload Data之外的帧内容的,也就是无法自定义opcode。所以,也就实现不了WebSocket规范定义的ping/pong协议。

此时,我们就可以把ping/pong当成一种用来解决特定问题的设计模式。既然我们只能自定义Payload Data的内容,那么我们可以简单的在Payload Data里面添加一个字段用于区分是ping/pong帧,还是普通的数据帧,比如type。当type字段是ping/pong的时候表明是ping/pong帧,如果是其他字段才是普通的数据帧。


如何使ERROR_INTERNET_DISCONNECTED错误信息不显示在控制台

当断网的时候,连接WebSocket会发现浏览器控制台会log一个错误信息:

WebSocket connection to 'ws://...' failed: Error in connection establishment: net::ERR_INTERNET_DISCONNECTED

原先的开发经验是,控制台如果有报错的话,肯定是代码某个地方有错误,并且没有被我们的代码捕获到,所以就会在控制台抛出,如果使用了try catch 或者全局的window.onerror捕获到了错误信息,就不会在控制台打印了。所以,我就尝试了上述方法,发现捕捉不到,还是会在控制台log。

另外,WebSocket提供了两个事件,onerror和onclose。当发生上述错误信息的时候,onerror和onclose是会被调用的。但是,此时控制台还是会有上述报错信息。

经过一番查找,发现无法阻止上述错误信息显示在控制台。

那么,为什么浏览器会设计这样的行为呢?猜测原因如下:
上面说到通过onerror和onclose事件是可以捕捉到WebSocket创建失败的,但是,查看这两个事件的参数,我们只能从中找到一个code是1006的属性,输出在控制台的错误信息ERR_INTERNET_DISCONNECTED在参数里面找不到。接着,看一下code1006相关的东西:

User agents must not convey any failure information to scripts in a way that would allow a script to distinguish the following situations:

*   A server whose host name could not be resolved.
*   A server to which packets could not successfully be routed.
*   A server that refused the connection on the specified port.
*   A server that failed to correctly perform a TLS handshake (e.g., the server certificate can't be verified).
*   A server that did not complete the opening handshake (e.g. because it was not a WebSocket server).
*   A WebSocket server that sent a correct opening handshake, but that specified options that caused the client to drop the connection (e.g. the server specified a subprotocol that the client did not offer).
*   A WebSocket server that abruptly closed the connection after successfully completing the opening handshake.

In all of these cases, the the WebSocket connection close code would be 1006, as required by WebSocket Protocol. 

Allowing a script to distinguish these cases would allow a script to probe the user's local network in preparation for an attack.

从上述规范可以看到,规范是禁止浏览器向脚本传递下述造成WebSocket连接失败的具体原因的,只允许向脚本传递一个1006的code码,否则,用户就可以探测到局部网的信息,进而发起攻击。举个例子,上面那种断网的情况,脚本中只能得到1006的状态码,比如下面这种报错

Error in connection establishment: net::ERR_CONNECTION_REFUSED

也只能从onerror中获得一个1006的code码。

所以,作为开发人员,浏览器要怎么在告诉我们具体的错误信息的同时又阻止有可能发生的攻击呢?答案就是在控制台把具体的错误信息log出来。


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/10058

关闭

WebSocket断开原因、心跳机制防止自动断开连接

WebSocket断开的原因有很多,最好在WebSocket断开时,将错误打印出来。WebSocket断开时,会触发CloseEvent, CloseEvent会在连接关闭时发送给使用 WebSockets 的客户端. 它在 WebSocket 对象的 onclose 事件监听器中使用。

WebSocket理解与使用

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据

WebSocket 与 Polling , Long-Polling , Streaming 的比较!

Web Sockets定义了一种在通过一个单一的 socket 在网络上进行全双工通讯的通道。它不仅仅是传统的 HTTP 通讯的一个增量的提高,尤其对于实时、事件驱动的应用来说是一个飞跃。

理解WebSocket心跳及重连机制

在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务器端并没有触发onclose的事件。这样会有:服务器会继续向客户端发送多余的链接,并且这些数据还会丢失

WebSocket以及socketIO的使用

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

websocket的用途/场景

先总结:高即时性服务,比如聊天室的群聊,server顺序收到了张三,李四的消息,立即就推送给王五,不能让王五等半天。Ajax也可以一秒一刷,让王五去问张三说话没,如果张三10分钟没说话

websocket无法注入问题解决方案

websocket服务端往往需要和服务层打交道,因此需要将服务层的一些bean注入到websocket实现类中使用,但是呢,websocket实现类虽然顶部加上了@Component注解,依然无法通过@Resource和@Autowire注入spring容器管理下的bean

WebSocket 原理浅析与实现简单聊天

随着 Web 的发展,用户对于 Web 的实时推送要求也越来越高,在 WebSocket 出现之前,大多数情况下是通过客户端发起轮询来拿到服务端实时更新的数据,因为 HTTP1.x 协议有一个缺陷就是通信只能由客户端发起,服务端没法主动给客户端推送

vue封装websocket心跳包

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据

websocket 断线重连

服务端为swoole 的websocket;onclose、onerror出现两个,tcp重连的时候会重连两次;为避免这种情况,需要进行加锁lockReconnect;limitConnect 断线重连次数;

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!