HTML5 WebSocket 详解及使用
1.WebSocket 是什么?
WebSocket 是 HTML5 提供的一种在单个 TCP 连接上进行全双工通讯的协议。(双向通信协议)
2.WebSocket 的作用?
实现客户端与服务器之间的双向通信,允许服务端主动向客户端推送数据。
在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
3.WebSocket 和 HTTP 区别?
WebSocket 与 HTTP的关系图:
相同点:
1. WebSocket 都是一样基于 TCP 的可靠性传输协议,都是应用层协议
异同点:
1. WebSocket 可以双向发送或接受信息,而 HTTP 是单向的(HTTP 通信只能由客户端发起,不具备服务器主动推送能力);
2. WebSocket 的使用,需要先进行一次 客户端与服务器的握手,两者建立连接后才可以正常双向通信,而 HTTP 是一个 主动的 Request 对应一个 被动的Response;
4.WebSocket 的协议标识符?
如果服务器网址是 HTTP 那么 WebSocket 对应的是 ws
如果服务器网址是 HTTPS 加密的 那么 WebSocket 对应的是 wss
5.WebSocket 的作用总结?
WebSocket 是为了能够实现在 web 应用上与服务器进行双向通信的需求 而产生出来的协议,
相比于轮询 HTTP 请求的方式,WebSocket 节省了服务器资源,有效的提高了效率。
6.HTML5 WebSocket的使用方式描述
通过JavaScript中的WebSocket对象来创建WebSocket连接,并通过send()方法向服务器发送数据,通过onmessage()方法接收服务器返回的数据。
目前,WebSocket协议已经广泛应用于实时聊天、在线游戏、语音视频聊天、股票行情等领域。
7.WebSocket协议的特点
最大特点:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
其他特点:
(1)实时性:与传统的 HTTP 请求/响应模式不同,WebSocket 允许实时双向通信,使得服务器能够主动向客户端推送数据,而不需要客户端发起请求。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)服务器压力减少:WebSocket 连接保持打开状态,因此服务器不需要为每个请求创建一个新的连接。这可以减轻服务器的负载并提高性能。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws
(如果加密,则为wss
),服务器网址就是 URL。
(7)只需要经过一次 HTTP 请求,就可以一直传送消息(也称为回调)。
8.WebSocket 工作原理
WebSocket 的工作原理可以分为三个阶段:握手、数据传输和断开连接。
- 握手:客户端发起 WebSocket 连接时,通过向服务器发送一个特殊的 HTTP 请求头来建立连接。服务器检查请求头中的特定字段,确认支持 WebSocket 协议后,发送特殊的 HTTP 响应头进行握手确认。握手成功后,双方建立了 WebSocket 连接,可以进行后续的数据传输。
- 数据传输:一旦建立了 WebSocket 连接,客户端和服务器可以通过该连接进行双向的实时数据传输。双方可以发送和接收消息,消息以帧的形式进行传输。WebSocket 协议定义了不同类型的帧,如文本帧和二进制帧,用于传输不同类型的数据。
- 断开连接:当连接不再需要时,客户端或服务器可以发起关闭连接的请求。双方会交换特殊的关闭帧,以协商关闭连接,并确保双方都接收到了关闭请求。
9.WebSocket连接的过程
首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;
然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;
最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信
10、HTML5 WebSocket 需要后端配合吗
是的,
HTML5 WebSocket 需要后端配合。具体来说,WebSocket 需要在后端实现一个 WebSocket 服务器,该服务器能够接收 WebSocket 请求,并在客户端和服务器之间建立 WebSocket 连接。通常情况下,后端可以使用一些 WebSocket 服务器库(如 Node.js 的 Socket.IO 或 Java 的 Java-WebSocket)来实现 WebSocket 服务器。在客户端,HTML5 提供了 WebSocket API,让客户端能够与服务器建立 WebSocket 连接、发送和接收 WebSocket 消息。因此,HTML5 WebSocket 需要客户端和服务器的配合。
以上原理相关内容结束,以下就是实际使用相关的内容
11.客户端API
WebSocket 构造函数 |
|
执行上面语句之后,客户端就会与服务器进行连接。 | |
Socket.readyState | 获取实例的当前链接状态 |
|
示例:
|
webSocket.onopen |
用于指定连接成功后的回调函数 |
如果要指定多个回调函数,可以使用
|
|
webSocket.onclose |
用于指定连接关闭后的回调函数 |
|
|
webSocket.onmessage |
用于指定收到服务器数据后的回调函数 |
注意,服务器数据可能是文本,也可能是二进制数据(
除了动态判断收到的数据类型,也可以使用
|
|
webSocket.send() |
向服务器发送数据 |
发送文本的例子。
发送 Blob 对象的例子。
发送 ArrayBuffer 对象的例子。
|
|
webSocket.bufferedAmount |
表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。 |
|
|
webSocket.onerror |
用于指定报错时的回调函数 |
|
代码实例:
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>菜鸟教程(runoob.com)</title> <script type="text/javascript"> function WebSocketTest() { if ("WebSocket" in window) { alert("您的浏览器支持 WebSocket!"); // 打开一个 web socket var ws = new WebSocket("ws://localhost:9998/echo"); ws.onopen = function() { // Web Socket 已连接上,使用 send() 方法发送数据 ws.send("发送数据"); alert("数据发送中..."); }; ws.onmessage = function (evt) { var received_msg = evt.data; alert("数据已接收..."); }; ws.onclose = function() { // 关闭 websocket alert("连接已关闭..."); }; } else { // 浏览器不支持 WebSocket alert("您的浏览器不支持 WebSocket!"); } } </script> </head> <body> <div id="sse"> <a href="javascript:WebSocketTest()">运行 WebSocket</a> </div> </body> </html>
12.服务端的实现
常用的 Node 实现有以下三种。
13.应用场景
基于websocket
的事实通信的特点,其存在的应用场景大概有:
- 即时聊天通信
- 多玩家游戏
- 在线协同编辑/编辑
- 实时数据流的拉取与推送
- 体育/游戏实况
- 实时地图位置
- 即时
Web
应用程序:即时Web
应用程序使用一个Web
套接字在客户端显示数据,这些数据由后端服务器连续发送。在WebSocke
t中,数据被连续推送/传输到已经打开的同一连接中,这就是为什么WebSocket
更快并提高了应用程序性能的原因。 例如在交易网站或比特币交易中,这是最不稳定的事情,它用于显示价格波动,数据被后端服务器使用Web套接字通道连续推送到客户端。 - 游戏应用程序:在游戏应用程序中,你可能会注意到,服务器会持续接收数据,而不会刷新用户界面。屏幕上的用户界面会自动刷新,而且不需要建立新的连接,因此在
WebSocket
游戏应用程序中非常有帮助。 - 聊天应用程序:聊天应用程序仅使用
WebSocket
建立一次连接,便能在订阅户之间交换,发布和广播消息。它重复使用相同的WebSocket
连接,用于发送和接收消息以及一对一的消息传输。
不能使用WebSocket的场景
如果我们需要通过网络传输的任何实时更新或连续数据流,则可以使用WebSocket
。如果我们要获取旧数据,或者只想获取一次数据供应用程序使用,则应该使用HTTP
协议,不需要很频繁或仅获取一次的数据可以通过简单的HTTP
请求查询,因此在这种情况下最好不要使用WebSocket
。
14.websocket 断线重连
1.如何判断在线离线?
概述:对两次请求的时间差与指定时间进行比较
- 当客户端第一次发送请求至服务端时会携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果不存在就存入db或者缓存中,
- 第二次客户端定再次发送请求依旧携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果存在就把上次的时间戳拿取出来,使用当前时间戳减去上次的时间,
- 得出的毫秒秒数判断是否大于指定的时间,若小于的话就是在线,否则就是离线;
2.如何解决断线问题
通过查阅资料了解到 nginx 代理的 websocket 转发,无消息连接会出现超时断开问题。网上资料提到解决方案两种,一种是修改nginx配置信息,第二种是websocket发送心跳包。
断线和重连
-
断线的可能原因1:websocket超时没有消息自动断开连接,应对措施:
这时候我们就需要知道服务端设置的超时时长是多少,在小于超时时间内发送心跳包,有两种方案:一种是客户端主动发送上行心跳包,另一种方案是服务端主动发送下行心跳包。
心跳包一般来说都是在逻辑层发送空的echo包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。
心跳检测步骤:
- 客户端每隔一个时间间隔发生一个探测包给服务器
- 客户端发包时启动一个超时定时器
- 服务器端接收到检测包,应该回应一个包
- 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
- 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
// 前端解决方案:心跳检测 var heartCheck = { timeout: 30000, //30秒发一次心跳 timeoutObj: null, serverTimeoutObj: null, reset: function(){ clearTimeout(this.timeoutObj); clearTimeout(this.serverTimeoutObj); return this; }, start: function(){ var self = this; this.timeoutObj = setTimeout(function(){ //这里发送一个心跳,后端收到后,返回一个心跳消息, //onmessage拿到返回的心跳就说明连接正常 ws.send("ping"); console.log("ping!") self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了 ws.close(); //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次 }, self.timeout); }, this.timeout); } }
-
断线的可能原因2:websocket异常包括服务端出现中断,交互切屏等等客户端异常中断等等
当若服务端宕机了,客户端怎么做?服务端再次上线时怎么做?
- 客户端则需要断开连接,通过onclose 关闭连接
- 服务端再次上线时则需要清除之间存的数据,若不清除 则会造成只要请求到服务端的都会被视为离线。
针对这种异常的中断解决方案就是处理重连,下面我们给出的重连方案是使用js库处理:引入reconnecting-websocket.min.js,ws建立链接方法使用js库api方法:
var ws = new ReconnectingWebSocket(url); // 断线重连: reconnectSocket(){ if ('ws' in window) { ws = new ReconnectingWebSocket(url); } else if ('MozWebSocket' in window) { ws = new MozWebSocket(url); } else { ws = new SockJS(url); } }
断网监测支持使用js库:offline.min.js
onLineCheck(){ Offline.check(); console.log(Offline.state,'---Offline.state'); console.log(this.socketStatus,'---this.socketStatus'); if(!this.socketStatus){ console.log('网络连接已断开!'); if(Offline.state === 'up' && websocket.reconnectAttempts > websocket.maxReconnectInterval){ window.location.reload(); } reconnectSocket(); }else{ console.log('网络连接成功!'); websocket.send("heartBeat"); } } // 使用:在websocket断开链接时调用网络中断监测 websocket.onclose => () { onLineCheck(); };
补充概念知识:
心跳是什么?
心跳是 websocket 中的一个概念,就是在 websocket 中用定时器,定时向服务器发送一个消息内容,我们约定,如果后端收到这个消息内容,就要向前端回一个消息内容。如果前端没有收到消息回复,则有可能 websocket 断掉了,那么就要进行重连操作,来保证我们的 websocket 处于连接状态。
let url = `${process.env['VUE_APP_WEBSOCKET']}/websocket` let ws = new WebSocket(url) ws.addEventListener('open', e => { console.log('长连接连接成功') // 执行心跳方法 dispatch('wsHeartStart') })
websocket
链接成功以后,开启心跳方法。心跳功能的实现如下:const state = { ws: null, // 心跳时间(s) wsTimeout: 20, // 等待心跳响应时间(s),等待心跳的响应时间要大于心跳时间5s以上 waitHeartTime: 25 } // 开启心跳 wsHeartStart({ state, dispatch }) { timer.wsTimeoutObj = setTimeout(() => { if(state.ws && state.ws.readyState == 1) { state.ws.send('') } else { dispatch('handlerWSError') } }, state.wsTimeout * 1000) timer.serverTimeoutObj = setTimeout(() => { console.log('接收心跳异常!') dispatch('handlerWSError') }, state.waitHeartTime * 1000) }
上面这个代码片段,描述了心跳的基本意思;就是用定时器去向服务端发送一个空的字符串,我们与后端约定,当后端
ws
收到空字符串的时候,要回一个字符串。如果我们没有收到,则判断为接收心跳异常
,准备重连机制。