多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## ## 一、为什么需要 WebSocket? 初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处? 答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。 举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。 这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用["轮询"](https://www.pubnub.com/blog/2014-12-01-http-long-polling/):每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。 轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。 ## 二、简介 WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。 它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于[服务器推送技术](https://en.wikipedia.org/wiki/Push_technology)的一种。 ![](http://www.ruanyifeng.com/blogimg/asset/2017/bg2017051502.png) 其他特点包括: (1)建立在 TCP 协议之上,服务器端的实现比较容易。 (2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。 (3)数据格式比较轻量,性能开销小,通信高效。 (4)可以发送文本,也可以发送二进制数据。 (5)没有同源限制,客户端可以与任意服务器通信。 (6)协议标识符是`ws`(如果加密,则为`wss`),服务器网址就是 URL。 > ~~~markup > > ws://example.com:80/some/path > > ~~~ ![](http://www.ruanyifeng.com/blogimg/asset/2017/bg2017051503.jpg) **长连接**:一个连接上可以连续发送多个数据包,在连接期间,如果没有数据包发送,需要双方发链路检查包。 **TCP/IP**:TCP/IP属于传输层,主要解决数据在网络中的传输问题,只管传输数据。但是那样对传输的数据没有一个规范的封装、解析等处理,使得传输的数据就很难识别,所以才有了应用层协议对数据的封装、解析等,如HTTP协议。 **HTTP**:HTTP是应用层协议,封装解析传输的数据。 从HTTP1.1开始其实就默认开启了长连接,也就是请求header中看到的Connection:Keep-alive。但是这个长连接只是说保持了(服务器可以告诉客户端保持时间Keep-Alive:timeout=200;max=20;)这个TCP通道,直接Request - Response,而不需要再创建一个连接通道,做到了一个性能优化。但是HTTP通讯本身还是Request - Response。 **socket**:与HTTP不一样,socket不是协议,它是在程序层面上对传输层协议(可以主要理解为TCP/IP)的接口封装。 我们知道传输层的协议,是解决数据在网络中传输的,那么socket就是传输通道两端的接口。所以对于前端而言,socket也可以简单的理解为对TCP/IP的抽象协议。 **WebSocket**: WebSocket是包装成了一个应用层协议作为socket,从而能够让客户端和远程服务端通过web建立全双工通信。websocket提供ws和wss两种URL方案。[协议英文文档](https://tools.ietf.org/rfc/rfc6455.txt)和[中文翻译](http://blog.csdn.net/stoneson/article/details/8063802) ## WebSocket API * * * 使用WebSocket构造函数创建一个WebSocket连接,返回一个websocket实例。通过这个实例我们可以监听事件,这些事件可以知道什么时候简历连接,什么时候有消息被推过来了,什么时候发生错误了,时候连接关闭。我们可以使用node搭建一个WebSocket服务器来看看,[github](https://github.com/daipeng7/websocket)。同样也可以调用[websocket.org](http://demos.kaazing.com/echo/)网站的demo服务器[demos.kaazing.com/echo/](http://demos.kaazing.com/echo/)。 ### 事件 ~~~ //创建WebSocket实例,可以使用ws和wss。第二个参数可以选填自定义协议,如果多协议,可以以数组方式 var socket = new WebSocket('ws://demos.kaazing.com/echo'); 复制代码 ~~~ * **open** 服务器相应WebSocket连接请求触发 ~~~ socket.onopen = (event) => { socket.send('Hello Server!'); }; 复制代码 ~~~ * **message** 服务器有 响应数据 触发 ~~~ socket.onmessage = (event) => { debugger; console.log(event.data); }; 复制代码 ~~~ * **error** 出错时触发,并且会关闭连接。这时可以根据错误信息进行按需处理 ~~~ socket.onerror = (event) => { console.log('error'); } 复制代码 ~~~ * **close** ~~~ 连接关闭时触发,这在两端都可以关闭。另外如果连接失败也是会触发的。 针对关闭一般我们会做一些异常处理,关于异常参数: 1. socket.readyState 2 正在关闭 3 已经关闭 2. event.wasClean [Boolean] true 客户端或者服务器端调用close主动关闭 false 反之 3. event.code [Number] 关闭连接的状态码。socket.close(code, reason) 4. event.reason [String] 关闭连接的原因。socket.close(code, reason) socket.onclose = (event) => { debugger; } 复制代码 ~~~ ### 方法 * **send** send(data) 发送方法 data 可以是String/Blob/ArrayBuffer/ByteBuffer等 需要注意,使用send发送数据,必须是连接建立之后。一般会在onopen事件触发后发送: ~~~ socket.onopen = (event) => { socket.send('Hello Server!'); }; 复制代码 ~~~ 如果是需要去响应别的事件再发送消息,也就是将WebSocket实例socket交给别的方法使用,因为在发送时你不一定知道socket是否还连接着,所以可以检查readyState属性的值是否等于OPEN常量,也就是查看socket是否还连接着。 ~~~ btn.onclick = function startSocket(){ //判断是否连接是否还存在 if(socket.readyState == WebSocket.OPEN){ var message = document.getElementById("message").value; if(message != "") socket.send(message); } } 复制代码 ~~~ * **close** 使用close(\[code\[,reason\]\])方法可以关闭连接。code和reason均为选填 ~~~ // 正常关闭 socket.close(1000, "closing normally"); 复制代码 ~~~ ### 常量 | 常量名 | 值 | 描述 | | --- | --- | --- | | CONNECTING | 0 | 连接还未开启 | | OPEN | 1 | 连接开启可以通信 | | CLOSING | 2 | 连接正在关闭中 | | CLOSED | 3 | 连接已经关闭 |