什么是 WebSocket
WebSocket
是HTML5
开始提供的一种在单个TCP
连接上进行双向通讯的协议。
WebSocket
使得客户端和服务器之间的数据交换变得更加简单,允许服务器主动向客户端推送数据。在WebSocket API
中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
WebSocket的特点
- 建立再TCP协议之上,一次连接成功便保持持久性的连接,服务器端的实现比较容易
- 与HTTP协议有着良好的兼容性。默认端口上80/443,并且握手阶段采用HTTP协议,因此握手时不容易屏蔽,能通过各种HTTP代理服务器。
- 协议标识符上ws/wss,普通80端口:ws://10.219.245.1:5560,加密443 端口:wss://10.219.245.1:5560
- 没有跨域问题,不同于HTTP,WebSocket没有同源限制,客户端可以与任意服务器通信
- 数据格式比较轻巧,新能开销小,通信高效
- 可以发送文本,也可以发送二进制数据
客户端API
WebSocket 构造函数
// WebSocket对象作为一个构造函数,用于新建WebSocket实例。创建实例,客户端就会与服务器进行连接。
var ws = new WebSocket("ws://localhost:8080");
WebSocket.readyState 返回当前WebSocket的连接状态,只读
- WebSocket.CONNECTING:值为0,表示正在连接中
- WebSocket.OPEN:值为1,表示已经连接成功,可以通信了
- WebSocket.CLOSING:值为2,表示连接正在关闭
- WebSocket.CLOSED:值为3,表示连接已关闭,或者没有连接成功
WebSocket.onopen 连接成功后的回调函数
ws.onopen = function () {
ws.send('在这里可以向服务器发送消息了');
};
// 如果需要指定多个回调函数,可以使用 addEventListener 方法进行绑定
ws.addEventListener('open', function (event) {
ws.send('Hello Server!');
});
WebSocket.onclose 连接关闭后的回调函数
ws.onclose = function (event) {
// 返回一个事件监听器,这个监听器将在WebSocket连接的readyState变为CLOSED时被调用。
}
// 指定多个回调函数
ws.addEventListener('close', function(event) {
// 遇到连接断开时可以在这来尝试重新连接
});
WebSocket.onmessage 客户端接收到服务端数据时触发的回调函数
ws.onmessage = function(event) {
var data = event.data; // 获取服务端的数据
// 处理数据
}
// 指定多个回调函数
ws.addEventListener('message', function(event) {
var data = event.data;
// 处理数据
});
// 动态判断收到的数据类型, 可以是文本(blob),也可能是二进制数据(Arraybuffer)
ws.onmessage = function(event) {
if(typeof event.data == String) {
console.log('文本类型数据')
}
if(event.data instanceof ArrayBuffer) {
var buffer = event.data;
console.log('二进制数据')
}
}
WebSocket.onerror 通信发生错误时触发的回调函数
ws.onerror = function(event) {
// 处理socket重连
}
ws.addEventListener("error", function() {
// 处理socket重连
});
WebSocket.send() 用于向服务器发送数据
ws.send('发送数据'); // 发送文本数据
// 发送 Blob 对象
var file = document.querySelector('input[type="file"]').files[0];
ws.send(file);
// 发送 ArrayBuffer 对象
var img = canvas_context.getImageData(0,0,400, 320);
var binary = new Uint8Array(img.data.length);
for(var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
ws.send(binary.buffer);
WebSocket.bufferedAmount 属性,表示还有多少字节的二进制数据没有发送出去。可以用来判断发送是否结束。
var data = new ArrayBuffer(1000000);
ws.send(data);
if(ws.bufferedAmount === 0) {
// 发送完毕
}else{
// 发送还没有结束
}
关于webSocket重连机制
在实际的运用中总是回遇到很多意外情况,包括网络不稳定、后端接口不稳定,或者长时间不与后台"联系"等因素,都有可能导致webSocket连接断开,所以想要一个稳定的webSocket连接,就必须有重连机制,webSocket在检测到连接断开时可以自动尝试重新连接,不影响数据的传输以及业务的开展。
var isReconnect = false; // 避免重复连接
var timer = null; // 避免重复连接,保证同一时刻只有一个重连
var ws = null;
// 重连方法
function reconnectWebSocket() {
console.log("重新连接WebSocket...");
if(isReconnect) {
// 如果连接上了就直接退出重连
return;
}
isReconnect = true;
// 如果没有连接上会一直重连,设置延迟避免请求过多
timer && clearTimeout(timer);
timer = setTimeout(() => {
initWebSocket(); // 初始化连接WebSocket
isReconnect = false;
}, 2000);
}
// 初始化连接WebSocket
function initWebSocket() {
var socketUrl = 'ws://url';
// 判断浏览器是否支持websocket,不支持给除提示
window.WebSocket = window.WebSocket || window.MozWebSocket;
if(!window.WebSocket) {
console.log("当前浏览器不支持websocket");
return;
}
// 创建实例,连接服务器
ws = new WebSocket(socketUrl);
// 连接成功,可以发送消息
ws.onopen = function(event) {};
// 收到服务器消息后触发
ws.onmessage = function(event) {};
// 连接断开,进行重连
ws.onclose = function(event) {
reconnectWebSocket();
}
// 发生错误时,进行重连
ws.onerror = function(event) {
reconnectWebSocket();
}
}
webSocket 心跳机制
webSocket重连机制,一般的网络断开,连接不稳定断开可以重连成功,但是有些情况是无法被检测到连接已经断开的,比如潮涌无线连接时,设备连着wifi,凡是wifi实际上已经没有流量或者跟网络的连接已经断开,webSocket是不会知道的,或者检测到了会一直不停重连,但是一直连不上,等到wifi恢复正常了也很大机率是连接失败。这个时候需要加上心跳机制,心跳是前端和后端的一种约定,前端每过一段时间就向后端发送规定格式的"心跳",后台收到这个心跳后,返回相应的信息,只要两端一直有心跳的交互就任务交互还存在。如果在约定时间内(比如2分钟)前端都没有收到后端返回的心跳信息,那么久可以认为这是一个有问题的连接,前端就主动断掉这次连接,重新发起一个webSocket连接。
webSocket 简单的封装
(function() {
// 消息推送
function ChatNotice(options) {
this.socket; // websocket对象
this.isConnection = false; // 是否连接服务器
this.timer = null;
this.loginData = options.login;
this.socketUrl = options.url;
this.sessionid = "";
// 初始化WebSocket连接
this.initConnection();
}
// 初始化WebSocket连接
ChatNotice.prototype.initConnection = function () {
var _that = this;
// 判断浏览器是否支持websocket
window.WebSocket = window.WebSocket || window.MozWebSocket;
if (!window.WebSocket) {
console.log("当前浏览器不支持websocket, 建议使用谷歌浏览器访问");
return;
}
// 连接服务器
this.socket = new WebSocket(this.socketUrl);
console.log("readyState:::", this.socket.readyState);
// 服务器连接成功后回调
this.socket.onopen = function(event) {
console.log(':::::::连接成功!!!:::::::');
_that.login();
};
// 收到服务器消息后回调
this.socket.onmessage = function(event) {
var data = JSON.parse(event.data);
_that.handleMessage(data);
}
// 连接断开后回调
this.socket.onclose = function(event) {
console.log(":::::::断开连接:::::::");
_that.againConnectionWebSocket();
};
// 报错时回调
this.socket.onerror = function(event) {
console.log(":::::::websocket遇到错误:::::::");
_that.againConnectionWebSocket();
}
return this;
}
// 发送消息给服务器进行登录
ChatNotice.prototype.login = function() {
var data = this.loginData;
if(data.order == undefined) {
data.order = 1;
}
if(this.sessionid) {
data.sessionid = this.sessionid;
}
console.log(':::::::发送消息:::::::', data);
// 向服务器发送消息
this.socket.send(JSON.stringify(data));
}
// 处理消息
ChatNotice.prototype.handleMessage = function(msg) {
console.log(':::::::处理消息:::::::', msg);
var head = msg.head;
var method = this.loginData.method;
if (head.method == method && head.errcode == 0) {
this.sessionid = msg.data.sessionid;
} else {
// 处理消息
}
}
// 断掉重新连接服务器
ChatNotice.prototype.againConnectionWebSocket = function() {
var _that = this;
if(this.isConnection) {
return;
}
console.log(':::::::尝试重新连接服务器:::::::');
this.isConnection = true;
this.timer && clearTimeout(this.timer);
this.timer = setTimeout(function() {
_that.initConnection();
this.isConnection = false;
}, 1000);
}
new ChatNotice({
url: "ws://10.219.245.1:5560",
login: {
method: 8533,
opname: socketAccount
}
});
}());