前言
其实很早就知道了 websocket 这个概念了, 像现在大火的直播等使用的就是 websocket. 之前找爬虫工作, 对面问我爬过 websocket 网站没, 很汗颜, 那时候还只是听说过. 现在想了解一下, 基于 JavaScript 语言.
Websocket 是什么
websocket 是一种网络通信协议, 运行在 TCP 协议之上.
对于传统的 HTTP 协议来说, 通信只能由客户端发起, 然后服务端响应. HTTP 协议做不到服务器制动向客户端推送信息. HTTP 协议的这种单向请求的特点, 注定了如果服务器有连续的状态变化, 客户端要获知就非常麻烦. 只能使用''轮询'':(非阻塞通信), 每隔一段时候, 就发出一个询问, 了解服务器有没有新的消息. 轮询的效率低, 非常浪费资源.
websocket 协议在 2008 年诞生, 它的最大特点就是服务器可以主动向客户端推送消息, 客户端也可以主动向服务器发送消息, 是真正的双向平等对话, 属于服务器推送技术的一种. websocket 允许服务器端与客户端进行全双工通信. 其特点如下:
建立在 TCP 协议之上, 服务端的实现比较容易;
与 HTTP 协议有着良好的兼容性. 默认端口也是 80 和 443, 并且握手阶段采用 HTTP 协议, 因此握手时不容易屏蔽, 能通过各种 HTTP 代理服务器;
数据个数比较轻量, 性能开销小, 通信高效;
可以发送文本, 也可以发送二进制数据;
没有同源限制, 客户端可以与任意服务器通信, 完全可以取代 Ajax;
协议标识符是 ws(加密就是 wss), 服务器网址为 URL.
ws://example.com:80/some/path
websocket 握手
浏览器发出的 websocket 握手请求类似于下面:
- GET / HTTP/1.1
- Connection: Upgrade
- Upgrade: websocket
- Host: example.com
- Origin: null
- Sec-WebSocket-Key: ...
- Sec-WebSocket-Version: 13
这是数据头, HTTP1.1 协议规定, Upgrade 字段表示将通信协议从 HTTP/1.1 转向该字段指定的协议. Connection 字段表示浏览器通知服务器, 如果可以的话就升级到 WebScoket 协议. Origin 字段用于提供请求发出的域名, 供服务器验证是否许可的范围内 (服务器可以不验证).Sec-Websocket-Key 则是用于握手协议的密钥, 是 Base64 编码的 16 字节随机字符串 (可以反解).
服务器的 WebSocket 回应如下:
- HTTP/1.1 101 Switching Protocols
- Connection: Upgrade
- Upgrade: websocket
- Sec-WebSocket-Accept: ...
- Sec-WebSocket-Origin: null
- Sec-WebSocket-Location: ws://exammple.com/
服务器同样用 Connection 字段通知浏览器, 可以改变协议为 Websocket,Sec-WebSocket-Accept 字段是服务器在浏览器提供的 Sec-Webscoket-Key 字符串后面, 添加 RFC6465(不了解) 标准规定的''258EAFA5-E914-47DA-95CA-C5AB0DC85B11''字符串, 然后再取 SHA-1 的哈希值. 浏览器将对这个值进行验证, 以证明确实是目标服务器回应了 Websocket 请求. Sec-WebSocket-Location 字段表示进行通信的 Websocket 网址.
完成握手以后, WebSocket 协议就在 TCP 协议之上, 开始传送数据.
客户端的示例
- var ws = new WebSocket('wss://echo.websocket.org'); // 网址不可达
- ws.open = function(ev) {
- console.log('Connection open...');
- ws.send('Hello WebSocket!');
- };
- ws.onmessage = function(ev) {
- console.log('Recevied Message:' + ev.data);
- };
- ws.onclose = function(ev) {
- console.log('Connection closed.');
- }
客户端 API
浏览器对 WebSocket 协议的处理, 无非是三件事:
建立连接和断开连接;
发送数据和接收数据;
处理错误.
构造函数 WebSocket
WebSocket 对象作为一个构造函数, 用于新建 WebSocket 实例.
var ws = new WebSocket('ws://localhost:8080');
执行上面语句后, 客户端就会与服务器进行连接.
WebSocket.readyState
跟 HTTP 协议一样, WebSocket 也有自己的状态码:
CONNECTING: 值为 0, 表示正在连接;
OPEN: 值为 1, 表示连接成功, 可以进行通信;
CLOSING: 值为 2, 表示连接正在关闭;
CLOSED: 值为 3, 表示连接已经关闭, 或者打开连接失败.
实例:
- switch (ws.readyState) {
- case WebSocket.CONNECTING:
- // do something
- break;
- case WebSocket.OPEN:
- // do something
- break;
- case WebSocket.CLOSING:
- // do something
- break;
- case WebSocket.CLOSED:
- // do something
- break;
- default:
- // this never happend
- break;
- }
- webSocket.onopen
实例对象的 onopen 属性, 用于指定连接成功后的回调函数.
- ws.onopen = function () {
- ws.send('Hello MUSIBII');
- }
如果要指定多个回调函数, 可以使用 addEventListener 方法.
- ws.addEventListener('open', function () {
- ws.send('Hello MUSIBII');
- })
- webSocket.onclose
实例对象的 onclose 属性, 用于指定连接关闭后的回调函数.
- ws.onclose = function (ev) {
- var code = ev.code;
- var reason = ev.reason;
- var wasClean = ev.wasClean;
- // handle close event
- };
- ws.addEnentListener('close', function (ev) {
- var code = ev.clde;
- var reason = ev.reason;
- var wasClean = ev.wasClean;
- // handle close event
- })
- webSocket.onmessage
实例对象的 onmessage 属性, 用于指定收到服务器数据后的回调函数.
- ws.onmessage = function (ev) {
- var data = ev.data;
- // handle data
- };
- ws.addEvendListener('message', function (ev) {
- var date = ev.data;
- // handle data
- })
注意: 服务器的数据可能是文本, 也可能是二进制数据 (blob 对象或 Arraybuffer 对象).
- ws.onmessage = function (ev) {
- if(typeof ev.data === String) {
- console.log('Received data string');
- }
- if(ev.data instanceof ArrayBuffer) {
- var buffer = ev.data;
- console.log('Received arraybuffer');
- }
- }
除了动态判断收到的数据类型, 也可以使用 binaryType 属性, 显式指定收到的二进制数据类型.
- // 收到的是 blob 数据
- ws.binaryType = 'blob';
- ws.onmessage = function (ev) {
- console.log(ev.data.size);
- };
- // 收到的是 ArrayBuffer 数据
- ws.binaryType = 'arraybuffer';
- ws.onmessage = function (ev) {
- console.log(ev.data.byteLength);
- }
- webSocket.send()
实例对象的 send() 方法用于向服务器发送数据.
发送文本的例子.
ws.send('your message');
发送 Blob 对象的例子.
- var file = document.querySelector('input[type ="file"]').files[0];
- ws.send(file);
发送 ArrayBuffer 对象的例子.
- // Senging 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.seng(binary.buffer);
- webSocket.bufferedAmout
实例对象的 bufferedAmout 属性, 表示还有多少字节的二级制数据没有发送出去. 它可以用来判断发送是否结束.
- var data = new ArrayBuffer(100000);
- socket.send(data);
- if (socket.bufferedAmount === 0) {
- // 发送完毕
- } else {
- // 发送没有结束
- }
- webSocket.onerror
实例对象的 onerror 属性, 用于指定报错时的回调函数.
- socket.onerror = function (ev) {
- // handle error event
- };
- socket.addEventListener('error', function (ev) {
- // handle error enent
- });
来源: https://www.cnblogs.com/zuanzuan/p/10162820.html