JavaScript之WebSocket技术详解金沙官网线上

> `WebSocket`协议可以实现前后端全双工通信,从而取代浪费资源的长轮询。在此协议的基础上,可以实现前后端数据、多端数据,真正的**实时响应**。在学习`WebSocket`的过程中,实现了一个简化版群聊,过程和代码详细记录在这篇文章中。 **本篇文章来自[董沅鑫的个人网站](https://godbmw.com/passage/38),引用、转载请指明出处**。 **查看更多知识,或者技术交流:请访问[`godbmw.com`](https://godbmw.com/)** ## 1 概述 ### 1.1 WebSocket 是什么? 1. 建立在 TCP 协议之上的网络通信协议 2. 全双工通信协议 3. 没有同源限制 4. 可以发送文本、二进制数据等 ### 1.2 为什么需要 WebSocket? 了解计算机网络协议的人,应该都知道:HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。 这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。 因此,如果在客户端想实时监听服务器变化,必须使用 ajax 来进行轮询,效率低,浪费资源。 而 websocket 就可以使得**前后端进行全双工通信(两方都可以向对方进行数据推送),是真正的平等对话**。 ## 2 WebSocket 客户端 支持`HTML5`的浏览器支持 WebSocket 协议: ```javascript var ws = new WebSocket(url); // 创建一个websocket对象

概述

``` ### 2.1 WebSocket 属性 | 属性 | 描述 | | ----------------- |

| | ws.readyState | 只读属性 readyState 表示连接状态,可以是以下值:0 - 表示连接尚未建立。1 - 表示连接已建立,可以进行通信。2 - 表示连接正在进行关闭。3 - 表示连接已经关闭或者连接不能打开。 | | ws.bufferedAmount | 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。 | ### 2.2 WebSocket 方法 | 属性 | 描述 | | ---------- | -------- | | ws.send() | 数据发送 | | ws.close() | 关闭连接 | ### 2.3 Websocket 事件 | 属性 | 描述 | | ------- | ------------ | | open | 连接建立触发 | | message | 通信时触发 | | error | 出错触发 | | close | 关闭连接触发 | ### 2.4 代码实现 假设我们在本地`8080`端口打开了websocket服务,那么,下面代码可以在浏览器中实现和这个服务的通信: ```html

``` ## 3 WebSocket 服务端 > 关于服务端实现,根据技术选型不同,可以选用不同的库和包。我这里使用的是`node`的`ws`库来websocket服务端。 在[阮一峰的博文](http://www.ruanyifeng.com/blog/2017/05/websocket.html)提到的`socket.io`库,在浏览器端的写法不兼容原生API,准确来说,它们自己实现了一套websocket。所以,使用的时候前后端都应该引用第三方库。**这样就造成了代码迁移性,严重下降。** 综上所述,`ws`库有以下优点: 1. 兼容性好,兼容浏览器原生API 2. 长期维护,效果稳定 3. 使用方便(往下看就知道了) ## 4 实现群聊 ### 4.1 群聊 服务端实现 首先,在命令行中,安装`ws`库: `npm install ws --save` 现在,利用`ws`来实现一个监听`8080`端口的websocket服务器,**讲解都在代码注释里,一目了然**: ```javascript const PORT = 8080; // 监听端口 const WebSocket = require("ws"); // 引入 ws 库 const wss = new WebSocket.Server({ port: PORT }); // 声明wss对象 /** * 向除了本身之外所有客户端发送消息,实现群聊功能 * @param {*} data 要发送的数据 * @param {*} ws 客户端连接对象 */ wss.broadcastToElse = function broadcast(data, ws) { wss.clients.forEach(function each(client) { if (client !== ws && client.readyState === WebSocket.OPEN) { client.send(data); } }); }; /* 客户端接入,触发 connection */ wss.on("connection", function connection(ws, req) { let ip = req.connection.remoteAddress; // 通过req对象可以获得客户端信息,比如:ip,headers等 /* 客户端发送消息,触发 message */ ws.on("message", function incoming(message) { ws.send(message); // 向客户端发送消息 wss.broadcastToElse(message, ws); // 向 其他的 客户端发送消息,实现群聊效果 }); }); ``` ### 4.2 群聊 客户端实现 为了方便编写,这里引入了`jquery`和`bootstrap`这两个库,只需要关注js代码即可。 ```html

群聊

发送

``` ### 4.3 群聊 效果展示 首先启动我们的服务端代码:`node server.js` 。其中,`server.js`是放置服务端代码的文件。 然后,我们打开2次编写的`html`代码,这相当于,打开2个客户端。来检测群聊功能。 ![效果图](https://raw.githubusercontent.com/dongyuanxin/markdown-static/master/JavaScript/websocket学习和群聊实现/1.png) ## 5. 相关资料 - 概念解释: - http://www.ruanyifeng.com/blog/2017/05/websocket.html - https://www.cnblogs.com/jingmoxukong/p/7755643.html - `ws`文档:https://www.npmjs.com/package/ws **本篇文章来自[董沅鑫的个人网站](https://godbmw.com/passage/38),引用、转载请指明出处**。 **查看更多知识,或者技术交流:请访问[`godbmw`](https://godbmw.com/)**

HTTP协议是一种无状态协议,服务器端本身不具有识别客户端的能力,必须借助外部机制,比如session和cookie,才能与特定客户端保持对话。这多多少少带来一些不便,尤其在服务器端与客户端需要持续交换数据的场合(比如网络聊天),更是如此。为了解决这个问题,HTML5提出了浏览器的WebSocket API

WebSocket的主要作用是,允许服务器端与客户端进行全双工(full-duplex)的通信。举例来说,HTTP协议有点像发电子邮件,发出后必须等待对方回信;WebSocket则是像打电话,服务器端和客户端可以同时向对方发送数据,它们之间存着一条持续打开的数据通道。

WebSocket协议完全可以取代Ajax方法,用来向服务器端发送文本和二进制数据,而且还没有“同域限制”。

WebSocket不使用HTTP协议,而是使用自己的协议。浏览器发出的WebSocket请求类似于下面的样子:

GET / HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Host: example.com
Origin: null
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13
上面的头信息显示,有一个HTTP头是Upgrade。HTTP1.1协议规定,Upgrade头信息表示将通信协议从HTTP/1.1转向该项所指定的协议。“Connection: Upgrade”就表示浏览器通知服务器,如果可以,就升级到webSocket协议。Origin用于验证浏览器域名是否在服务器许可的范围内。Sec-WebSocket-Key则是用于握手协议的密钥,是base64编码的16字节随机字符串。

服务器端的WebSocket回应则是

HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Origin: null
Sec-WebSocket-Location: ws://example.com/

服务器端同样用“Connection: Upgrade”通知浏览器,需要改变协议。Sec-WebSocket-Accept是服务器在浏览器提供的Sec-WebSocket-Key字符串后面,添加“258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 字符串,然后再取sha-1的hash值。浏览器将对这个值进行验证,以证明确实是目标服务器回应了webSocket请求。Sec-WebSocket-Location表示进行通信的WebSocket网址。

请注意,WebSocket协议用ws表示。此外,还有wss协议,表示加密的WebSocket协议,对应HTTPs协议。
完成握手以后,WebSocket协议就在TCP协议之上,开始传送数据。

WebSocket协议需要服务器支持,目前比较流行的实现是基于node.js的socket.io,更多的实现可参阅Wikipedia。至于浏览器端,目前主流浏览器都支持WebSocket协议(包括IE 10+),仅有的例外是手机端的Opera Mini和Android Browser。

客户端

浏览器端对WebSocket协议的处理,无非就是三件事:

***建立连接和断开连接
发送数据和接收数据
处理错误


建立连接和断开连接

首先,客户端要检查浏览器是否支持WebSocket,使用的方法是查看window对象是否具有WebSocket属性。

if(window.WebSocket != undefined) {

 // WebSocket代码

}

然后,开始与服务器建立连接(这里假定服务器就是本机的1740端口,需要使用ws协议)。

if(window.WebSocket != undefined) {

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

}

建立连接以后的WebSocket实例对象(即上面代码中的connection),有一个readyState属性,表示目前的状态,可以取4个值:

0: 正在连接
1: 连接成功
2: 正在关闭
3: 连接关闭
握手协议成功以后,readyState就从0变为1,并触发open事件,这时就可以向服务器发送信息了。我们可以指定open事件的回调函数。

connection.onopen = wsOpen;

function wsOpen (event) { 
console.log(‘Connected to: ‘ + event.currentTarget.URL); 
}

关闭WebSocket连接,会触发close事件。

connection.onclose = wsClose;

function wsClose () { 
console.log(“Closed”); 
}

connection.close();

发送数据和接收数据

连接建立后,客户端通过send方法向服务器端发送数据。

connection.send(message);
除了发送字符串,也可以使用 Blob 或 ArrayBuffer 对象发送二进制数据。

// 使用ArrayBuffer发送canvas图像数据

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];

}

connection.send(binary.buffer);
// 使用Blob发送文件 
var file = document.querySelector(‘input[type=”file”]').files[0]; 
connection.send(file);

客户端收到服务器发送的数据,会触发message事件。可以通过定义message事件的回调函数,来处理服务端返回的数据。

connection.onmessage = wsMessage;

function wsMessage (event) { 
console.log(event.data); 
}

上面代码的回调函数wsMessage的参数为事件对象event,该对象的data属性包含了服务器返回的数据。

如果接收的是二进制数据,需要将连接对象的格式设为blob或arraybuffer。

connection.binaryType = 'arraybuffer';

connection.onmessage = function(e) {
 console.log(e.data.byteLength); // ArrayBuffer对象有byteLength属性
};

处理错误

如果出现错误,浏览器会触发WebSocket实例对象的error事件。

本文由金沙官网线上发布于Web前端,转载请注明出处:JavaScript之WebSocket技术详解金沙官网线上

您可能还会对下面的文章感兴趣: