1. websocket背景
由于HTTP 协议有一个明显的缺陷:通信只能由客户端发起。
举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。
这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就只能使用"轮询":每隔一段时候,就发出一个请求,了解服务器有没有新的信息。最典型的场景就是聊天室。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。
WebSocket 就是基于这种场景诞生的。
2. websocket特点
websocket协议是一种浏览器的API,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行全双工、双向数据传输。
webSockets特点:
- 与http一样,建立在 TCP 协议之上,本质上一种计算机网络应用层的协议,用来弥补http协议在持久通信能力上的不足。
- 服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 使用了自定义协议,协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
例:
ws://example.com:80/some/path
wss://example.com:443/some/path
3. websocket基本使用
const url = "wss://echo.websocket.org"
// 创建WebSockt,实例一个对象,并传入要链接的url,执行该句之后,客户端就会与服务器进行连接。
var ws = new WebSocket(url);
ws.onopen = function(evt) {
console.log("Connection open ...");
// send()方法发送数据
ws.send("Hello WebSockets!");
};
// 服务器发来的消息时,会触发message事件
ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};
ws.onclose = function(evt) {
console.log("Connection closed.");
};
// 在发生错误时触发error事件,连接不能持续
ws.onerror = function(){
alter("发生错误");
}
4. websocket原理
-
HTTP1.0 :一个 Request 一个 Response
-
HTTP1.1(使用 keep-alive):在一个 HTTP 连接中,可以发送多个 Request,接收多个 Response, 并且Request = Response,也就是说一个 Request 只能有一个 Response。
-
WebSocket连接的过程是:
首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;
然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;
最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行WebSocket全双工通信。 -
首先我们来看个典型的 WebSocket 握手
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com
其中:
Upgrade: websocket Connection: Upgrade
这个就是 WebSocket 的核心了,告诉 Apache 、 Nginx 等服务器将要发起的请求要用 WebSocket 协议了
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Key 是一个 Base64 encode 的值,这个是浏览器随机生成的,验证WebSocket服务器。Sec-WebSocket-Key 的生成算法是拼接服务端和客户端生成的字符串,进行SHA1哈希算法,再用base64编码。
Sec-WebSocket-Protocol: chat, superchat
Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同 URL 下,不同的服务所需要的协议。
Sec-WebSocket-Version: 13
Sec-WebSocket-Version 是告诉服务器所使用的 WebSocket 协议版本
然后服务器会返回下列东西,表示已经接受到请求, 成功建立 WebSocket 啦!
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
其中:
Upgrade: websocket Connection: Upgrade
告诉客户端即将升级的是 WebSocket 协议
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key 。
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Protocol 则是表示最终使用的协议。
至此,HTTP 已经完成它所有工作了,接下来就是完全按照 WebSocket 协议进行了。
5. 心跳包机制
在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务器端并没有触发onclose的事件。这样服务器会继续向客户端发送数据,并且这些数据还会丢失。所以就需要一种机制【心跳包机制】来检测客户端和服务端是否处于正常的链接状态,并且可用于维持长连接,保活。⼼跳包机制就是每隔⼀段时间会向服务器发送⼀个数据包,同时服务器端会回传⼀个数据包给客户端,来检测客户端和服务端是否处于正常的链接状态,否则的话,有可能是⽹络断开连接了。需要重连。
心跳包机制有2种方案:一种是客户端主动发送上行心跳包,另一种方案是服务端主动发送下行心跳包。
心跳检测步骤:
- 客户端每隔一个时间间隔发生一个探测包给服务器
- 客户端发包时启动一个超时定时器
- 服务器端接收到检测包,应该回应一个包
- 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
- 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
// 前端解决方案:心跳检测
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);
}
}