WebSocket
WebSocket
类似于标准的TCP连接,它是IETF(RFC 6455)定义的通过TCP进行实时全双工通信一种通信方式,这意味这它的功能更强大,常用于如股票报价器,聊天应用。
相比于SSE,它不仅可以双向通信,而且甚至还能处理音频/视频等二进制内容。
原理:
WebSocket协议是借用HTTP协议的101 switchprotocol(服务器根据客户端的指定,将协议转换成为 Upgrade首部所列的协议)来达到协议转换的,从HTTP协议切换成WebSocket通信协议。
优点:
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
其他特点包括:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws
(如果加密,则为wss
),服务器网址就是 URL。
缺点:
websocket 是长连接,受网络限制比较大,需要处理好重连,比如用户进电梯或电信用户打个电话网断了,这时候就需要重连,如果 ws 一直重连不上,有些较复杂的业务方会不愿意的。
websocket 很多坑,如果只是单页面还好,涉及到多页面,定时推送,复杂的推送,就非常容易出问题了,不管是前端,还是服务端都会遇到很多很多问题
一般有实时数据要求的会考虑 websocket
具体连接方式:
通过在请求头中增加 upgrade:websocket 及通信密钥(Sec-WebSocket-Key),使双方握手成功,建立全双工通信
具体连接方式:
通过在请求头中增加 upgrade:websocket 及通信密钥(Sec-WebSocket-Key),使双方握手成功,建立全双工通信。
(WebSocket客户端连接报文)
(WebSocket服务端响应报文)
- 通信过程:
websocket是纯事件驱动的,一旦 WebSocket 连接建立后,通过监听事件可以处理到来的数据和改变的连接状态。数据都以帧序列的形式传输。服务端发送数据后,消息和事件会异步到达。WebSocket编程遵循一个异步编程模型,只需要对WebSocket对象增加回调函数就可以监听事件。
websocket 掉线重连(心跳包)
websocket超时没有消息自动断开连接,应对措施:
这时候我们就需要知道服务端设置的超时时间是多少,在小于超时时间内发送心跳包,有2中方案,一种是客户端主动发送上行心跳包,另一种方案是服务端主动发送下行心跳包。下面主要讲一下客户端也就是前端如何实现心跳包:。
首先了解一下心跳包机制
心跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。
在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。
心跳包一般来说都是在逻辑层发送空的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)
}
}