之前利用websocket以及jQuery做了一个聊天通讯应用,最近在总结整个过程中的一些问题,也借此机会聊聊websocket协议。
webSocket本身不存在跨域问题,所以可以利用webSocket来进行非同源之间的通信。
webSocket协议
传统的http协议有着一个缺陷:其模型是客户端请求,服务器响应的形式,并且连接完之后就断开,这种就造成了一个问题,服务端可不可以在实现长连接的同时,让响应将数据发送呢?诚然,目前http2.0也推出了服务器推送的功能,但是目前http2.0并未完全取代http1.1。由于上面的问题,webSocket协议就应运而生。
webSocket协议与http协议
Webscoket是Web浏览器和服务器之间的一种全双工通信协议,与http协议相比
共同点:
1、都是基于TCP协议的;
2、都是客户端-服务器模型;
3、默认端口都是80或者443(ws,http:80,wss,https:443;)
4、webSocket协议是基于http的;(个人观点:我并不确定这个说法是否准确,但是我个人感觉可以这样说)
不同点:
1、webSocket协议可以服务器主动推数据;(最大的特点)
2、没有同源限制;
3、数据格式多、效率高;
4、
这里主要说一下,我上面说的第4点共同点,我主要是从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
服务器响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
根据客户端请求,我们可以看出实际上发出的还是http请求,但是在http握手完成后,由于请求中携带着Upgrade、Connection字段会将http协议升级成webSocket协议。在响应中,状态码以及响应字符串分别是101、Switching Protocols。另外,需要注意的是Sec-WebSocket-Key字段是很重要的,目前还没整理完,后续慢慢补上。
基于上面的分析,个人觉得webSocket协议是基于http的。
心跳检测与重连机制
由于webSocket是面向客户端与服务端且双方可以互相发送数据,那么保持连接没有断开是极为重要的一点。webSocket利用心跳检测来判断连接状态,如果发生了断开,那么就启用重连机制,让客户端-服务器继续保持连接。
心跳检测:每隔一个时间,发一次消息,检测连接状态。
先来上一段代码,后面再做分析
<html>
<head>
<meta charset="utf-8">
<title>WebSocket Demo</title>
</head>
<body>
<script type="text/javascript">
var lockReconnect = false;//避免重复连接
var url = "http://localhost:3000";
var ws;
var tt;
function createWebSocket() {
try {
socket = io(url);
init();
} catch(e) {
reconnect(url);
}
}
function init() {
socket.onclose = function () {
reconnect(url);
};
socket.onerror = function() {
reconnect(url);
};
socket.onopen = function () {
//心跳检测重置
heartCheck.start();
};
socket.onmessage = function (e) {
//拿到任何消息都说明当前连接是正常的
console.log('接收到消息');
heartCheck.start();
}
}
function reconnect(url) {
if(lockReconnect) {
return;
};
lockReconnect = true;
//没连接上会一直重连,设置延迟避免请求过多
if(tt){clearTimeout(tt)}
tt = setTimeout(function () {
createWebSocket(url);
lockReconnect = false;
}, 3000);
}
//心跳检测
var heartCheck = {
timeout: 3000,
timer : null,
serverTimeoutObj: null,
start: function(){
var self = this;
this.timer && clearTimeout(this.timer );
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timer = setTimeout(function(){
//心跳消息,后端收到返回心跳消息,
socket.send("HeartBeat");
self.serverTimeoutObj = setTimeout(function() {
socket.close()
// 超时重连
}, self.timeout);
}, this.timeout)
}
}
createWebSocket(url);
</script>
</body>
</html>
首先,我们分析一下连接断开的原因
1、正常断开,利用监听close函数,可以进行重连;
2、报错断开,利用监听error函数,可以进行重连;
3、非正常断开,比如网络问题,前端问题或者后端问题,都会造成一个结果,那就是发出消息无法收到回应,这就是心跳检测的原理。当一方发出消息之后,另一方必须做出回应,通过message进行监听,如果没有收到消息或者消息超时,则默认连接断开,调用close函数,而websocket监听到close函数,立即重新连接。如果收到消息直接重新计时。
未完待续~~~~
参考文章:
掘金:前端必备 HTTP 技能之 WebSocket 协议详解
思否:理解WebSocket心跳及重连机制(五)