WebSocket 了解一下

543 阅读6分钟

一、websocket是什么

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

首先,要明白WebSocket是一种通信协议,区别于HTTP协议,HTTP协议只能实现客户端请求,服务端响应的这种单项通信。 而WebSocket可以实现客户端与服务端的双向通讯,说白了,最大也是最明显的区别就是可以做到服务端主动将消息推送给客户端。

二、几种与服务端实时通信的方法

我们都知道,不使用WebSocket与服务器实时交互,一般有两种方法。

  1. AJAX轮询
  2. Long Polling长轮询。

AJAX轮询

AJAX轮询也就是定时发送请求,也就是普通的客户端与服务端通信过程,只不过是无限循环发送,这样,可以保证服务端一旦有最新消息,就可以被客户端获取。

Long Polling长轮询

Long Polling长轮询是客户端和浏览器保持一个长连接,等服务端有消息返回,断开。 然后再重新连接,也是个循环的过程,无穷尽也。。。

客户端发起一个Long Polling,服务端如果没有数据要返回的话, 会hold住请求,等到有数据,就会返回给客户端。客户端又会再次发起一次Long Polling,再重复一次上面的过程。

缺点 上边这两种方式都有个致命的弱点,开销太大,被动性。假设并发很高的话,这对服务端是个考验。 而WebSocket一次握手,持久连接,以及主动推送的特点可以解决上边的问题,又不至于损耗性能。

http https ws wss 四者区别

三、客户端的 API

3.1 WebSocket 构造函数

WebSocket 对象作为一个构造函数,用于新建 WebSocket 实例。

var ws = new WebSocket('ws://localhost:8080');

执行上面语句之后,客户端就会与服务器进行连接。

协议升级

首先,客户端发起协议升级请求。可以看到,采用的是标准的HTTP报文格式,且只支持GET方法。

GET / HTTP/1.1
Host: localhost:8080
Origin: http://127.0.0.1:3000
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==
//重点请求首部意义如下:

Connection: Upgrade:表示要升级协议
Upgrade: websocket:表示要升级到websocket协议。
Sec-WebSocket-Version: 13:表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。
Sec-WebSocket-Key:与后面服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。

3.2 webSocket.readyState

readyState属性返回实例对象的当前状态,共有四种。

CONNECTING:值为0,表示正在连接。
OPEN:值为1,表示连接成功,可以通信了。
CLOSING:值为2,表示连接正在关闭。
CLOSED:值为3,表示连接已经关闭,或者打开连接失败

3.3 webSocket.onopen

实例对象的onopen属性,用于指定连接成功后的回调函数。

ws.onopen = function () {
  ws.send('Hello Server!');
}
//如果要指定多个回调函数,可以使用addEventListener方法。
ws.addEventListener('open', function (event) {
  ws.send('Hello Server!');
});

3.4 webSocket.onclose

实例对象的onclose属性,用于指定连接关闭后的回调函数。

ws.onclose = function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
};

ws.addEventListener("close", function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
});

3.5 webSocket.onmessage

实例对象的onmessage属性,用于指定收到服务器数据后的回调函数。

ws.onmessage = function(event) {
  var data = event.data;
  // 处理数据
};

ws.addEventListener("message", function(event) {
  var data = event.data;
  // 处理数据
});
// 注意,服务器数据可能是文本,也可能是二进制数据(blob对象或Arraybuffer对象)
ws.onmessage = function(event){
  if(typeof event.data === String) {
    console.log("Received data string");
  }

  if(event.data instanceof ArrayBuffer){
    var buffer = event.data;
    console.log("Received arraybuffer");
  }
}

3.6 webSocket.send()

实例对象的send()方法用于向服务器发送数据。

  • 发送文本的例子。
ws.send('your message');
  • 发送 Blob 对象的例子。
var file = document
  .querySelector('input[type="file"]')
  .files[0];
ws.send(file);
  • 发送 ArrayBuffer 对象的例子。
// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
  binary[i] = img.data[i];
}
ws.send(binary.buffer);

3.7 webSocket.bufferedAmount

实例对象的bufferedAmount属性,表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。

var data = new ArrayBuffer(10000000);
socket.send(data);

if (socket.bufferedAmount === 0) {
  // 发送完毕
} else {
  // 发送还没结束
}

3.8 webSocket.onerror

实例对象的onerror属性,用于指定报错时的回调函数

var data = new ArrayBuffer(10000000);
socket.send(data);

if (socket.bufferedAmount === 0) {
  // 发送完毕
} else {
  // 发送还没结束
}

四、总结

  • 可以做到服务端主动将消息推送给客户端。
  • 数据格式轻量,性能开销小。客户端与服务端进行数据交换时,服务端到客户端的数据包头只有2到10字节,客户端到服务端需要加上另外4字节的掩码。HTTP每次都需要携带完整头部。
  • 更好的二进制支持,可以发送文本,和二进制数据
  • 没有同源限制,客户端可以与任意服务器通信
  • 协议标识符是ws(如果加密,则是wss),请求的地址就是后端支持websocket的API。
  • 因为 HTTP 协议有一个缺陷:通信只能由客户端发起
  • 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。 如:ws://example.com:80/some/path
  • 最典型的场景就是聊天室

五、参考

最后

最近在做一个聊天室项目,当中就是通过先发送http请求,获取用户的token之类信息,然后建立websocket.

WebSocket可写的东西还挺多,比如WebSocket扩展。客户端、服务端之间是如何协商、使用扩展的。WebSocket扩展可以给协议本身增加很多能力和想象空间,比如数据的压缩、加密,以及多路复用,以及最初的提案是对数据进行加密处理。基于安全、效率的考虑,最终采用了折中的方案:对数据载荷进行掩码处理等。

篇幅所限,这里先不展开,感兴趣的同学可以留言交流。文章如有错漏,敬请指出