一、什么是websocket?
-
因为http 通信只能由客户端发起,服务器返回查询结果,HTTP 协议做不到服务器主动向客户端推送信息,服务器有连续的状态变化,客户端要获知就非常麻烦。
-
我们只能使用
轮询
:每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。 -
轮询的效率低,非常浪费资源(因为必须不停连接,或者
HTTP 连接始终打开
); -
WebSocket
是一种支持双向通讯
网络通信协议。就是服务器可以主动向客户端推送信息
,客户端也可以主动向服务器发送信息
,属于服务器推送技术的一种.
二、websocket的特点
(1)建立在 TCP 协议
之上,服务器端的实现比较容易。
(2)与 HTTP 协议
有着良好的兼容性。默认端口也是80和443,并且握手阶段
采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器
。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本
,也可以发送二进制数据
(blob
对象或Arraybuffer
对象)
(5)收到的数据类型 可以使用binaryType 指定, 显式指定收到的二进制数据类型
(6)没有同源限制
,客户端可以与任意服务器通信。
(7)协议标识符是ws(握手http)
(如果加密,则为wss(tcp +TLS)
),服务器网址就是 URL。
三、使用示例代码
let webSocket = null;
let lockReconnect = false;
let userId = Math.random()*1000;
let toUserId = Math.random()*1000;
const pageInfo = {
'userId': userId, 'toUserId': toUserId, // 可多个用户同时连接
pageActive: 0,
video: 'pause', // play/pause
tabsInfo: {
page0: 0,
page1: 0,
}
}
webSocketInit();
//心跳检测
let heartCheck = {
timeout: 5000,
timeoutObj: null,
serverTimeOutObj: null,
start: function () {
console.log('start');
this.timeoutObj && clearTimeout(this.timeoutObj);
this.timeoutObj = setTimeout(function () {
console.log('start心跳');
if (webSocket.readyState == 1) {
webSocket.send(JSON.stringify(pageInfo))
}
// 再设置个定时器,如果一直没连接上,就关闭websocket。webSocket.close();
self.serverTimeOutObj = setTimeout(() =>
{
ws.onclose();
}, this.timeout)
}, this.timeout)
}
}
function webSocketInit () {
try {
if ('WebSocket' in window) {
webSocket = null
webSocket = new WebSocket(
// 此处填写你要连接的ws地址
// TODO 互联网版本地址
'wss://txw.lshr.cn:8011/websocket/' + userId
)
} else {
alert('该浏览器不支持websocket!');
}
} catch (e) {
websocketReconnect(); //尝试重连websocket
}
webSocket.onopen = function () {
/* * 连接成功 * */
console.log('通讯开始')
heartCheck.start();
}
webSocket.onmessage = function () {
// 收到消息时回调函数
heartCheck.start();
}
webSocket.onclose = function (event) {
console.log('连接关闭', event)
lockReconnect = false;
websocketReconnect(); //尝试重连websocket
}
webSocket.onerror = function (err) {
console.log('err: ', err);
lockReconnect = false;
websocketReconnect(); //尝试重连websocket
}
window.onbeforeunload = function () {
webSocket.close();
}
}
/**
* 重连ws
*/
function websocketReconnect () {
if (lockReconnect) { // 是否已经执行重连
return;
}
lockReconnect = true;
//没连接上会一直重连,设置延迟避免请求过多
tt && clearTimeout(tt);
var tt = setTimeout(function () {
webSocketInit();
lockReconnect = false;
console.log('正在重连');
}, 3000)
}
四、为什么需要WebSocket心跳检测
在实际应用中,可能会面临多种网络状况,例如:
1、客户端网络故障,可能会导致客户端无法发送和接收数据;
2、服务器网络故障,可能会导致客户端无法连接服务器;
3、防火墙或者代理等网络设备可能会拦截WebSocket通信,从而导致连接中断。
4、20分钟不给客户端send消息,连接就会自动断开连接。
为了应对这些可能的网络问题,我们需要使用WebSocket心跳检测机制来检测客户端的在线状态,避免无法处理掉线等异常情况。
心跳检测,重连概述
心跳检测:websocket连接成功之后,给服务器send一个消息,服务器会相应返回消息;收到消息之后又给给服务器send一个消息,如此往复,一直保持心跳。(注意:实际在开发中,我并没有在收到消息时又send消息,只是在websocket关闭之后,重连)
重连:websocket关闭之后,需要重新连接。
WebSocket心跳检测的注意事项
在使用WebSocket心跳检测时,我们需要注意以下几个问题:
1、心跳包的发送间隔应该根据具体情况进行调整,一般建议不要过长,例如30秒或者1分钟。
2、心跳包的消息内容应该尽量精简,避免浪费带宽;
3、心跳间隔和最长超时时间的设置需要考虑网络状况和服务器负载等因素;
4、在检测到客户端掉线时,需要及时关闭WebSocket连接,并且从客户端信息列表中删除该客户端。
其它注意事项:
https协议 对应 wws
http协议对应ws
// 开发过程因为没有在状态码1001情况下send消息,导致报错(状态码1002)
// send需要在websocket正常连接时才可以
// 发送JSON.stringify数据(如果不发送JSON.stringify类型的数据的话,需要服务端设置下)
if (webSocket.readyState == 1) {
webSocket.send(JSON.stringify(pageInfo))
}
五、 websocket在线测试
六、状态码
状态码 | 名称 | 描述 |
---|---|---|
0–999 | - | 保留段, 未使用。 |
1000 | CLOSE_NORMAL | 正常关闭; 无论为何目的而创建, 该链接都已成功完成任务。 |
1001 | CLOSE_GOING_AWAY | 终端离开, 可能因为服务端错误, 也可能因为浏览器正从打开连接的页面跳转离开。 |
1002 | CLOSE_PROTOCOL_ERROR | 由于协议错误而中断连接。 |
1003 | CLOSE_UNSUPPORTED | 由于接收到不允许的数据类型而断开连接 (如仅接收文本数据的终端接收到了二进制数据)。 |
1004 | - | 保留。 其意义可能会在未来定义。 |
1005 | CLOSE_NO_STATUS | 保留。 表示没有收到预期的状态码。 |
1006 | CLOSE_ABNORMAL | 保留。 用于期望收到状态码时连接非正常关闭 (也就是说, 没有发送关闭帧)。 |
1007 | Unsupported Data | 由于收到了格式不符的数据而断开连接 (如文本消息中包含了非 UTF-8 数据)。 |
1008 | Policy Violation | 由于收到不符合约定的数据而断开连接。 这是一个通用状态码, 用于不适合使用 1003 和 1009 状态码的场景。 |
1009 | CLOSE_TOO_LARGE | 由于收到过大的数据帧而断开连接。 |
1010 | Missing Extension | 客户端期望服务器商定一个或多个拓展, 但服务器没有处理, 因此客户端断开连接。 |
1011 | Internal Error | 客户端由于遇到没有预料的情况阻止其完成请求, 因此服务端断开连接。 |
1012 | Service Restart | 服务器由于重启而断开连接。 [Ref] |
1013 | Try Again Later | 服务器由于临时原因断开连接, 如服务器过载因此断开一部分客户端连接。 [Ref] |
1014 | - | 由 WebSocket 标准保留以便未来使用。 |
1015 | TLS Handshake | 保留。 表示连接由于无法完成 TLS 握手而关闭 (例如无法验证服务器证书)。 |
1016–1999 | - | 由 WebSocket 标准保留以便未来使用。 |
2000–2999 | - | 由 WebSocket 拓展保留使用。 |
3000–3999 | - | 可以由库或框架使用。 不应由应用使用。 可以在 IANA 注册, 先到先得。 |
4000–4999 | - | 可以由应用使用。 |
七、nginx配置
地址:wss://txw.lsh.cn:8011/websocket/1
map $http_upgrade $upgrade {
default upgrade;
'' close;
}
location /websocket {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://websocket;
proxy_connect_timeout 86400s;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
upstream websocket {
server 10.84.97.192:8080;
}