websocket原理在之前的博客中生动形象的描述过了,这里大概介绍一下
百度对于websocket的解释是:WebSocket是一种在单个TCP连接上进行全双工通信的协议
也就是说websocket是基于TCP协议可以进行双向通信的一个工具
并且websocket在建立服务器和客户端直接的连接的时候只需要完成一次握手,即可实现双向通信,不像发送ajax协议需要实现三次握手四次挥手,并且websocket的实时性,和控制开销,更好的连接状态的保持都是优于ajax
websocket的诞生是人们为了解决实时推送技术,因为之前全是使用的ajax轮询,也就是使用ajax技术在特定的时间内发送http请求,然后服务器返回数据给客户端的浏览器,这样的效率其实很慢,因为ajax发送出去的http请求可能含有较长的头部,然而真正有效的数据可能仅仅是后面那一部分,所以浪费了服务器带宽,而且比较新一点的技术做轮询的是Comet,这种技术实现了双向通信,但是依然是含有较长的头部,浪费带宽
补充一点,websocket使用的是http的101状态码建立连接,如下图:
再补充一点,部分浏览器可能不支持websocket接口,你可以在浏览器中尝试实例,目前现代主流浏览器都支持websocket: Chrome, Mozilla, Opera 和 Safari
// 打开一个 web socket
var ws = new WebSocket("ws://后端提供的websocket地址");
ws.onopen = function()
{
// Web Socket 已连接上,使用 send() 方法发送数据
ws.send("发送数据");
alert("数据发送中...");
};
onopen
方法为打开websocket连接,其中ws
为上面创建的websocket的实例
send
方法为发送数据的方法,其中本次发送字符串"发送数据"内容
ws.onmessage = function (evt)
{
var received_msg = evt.data;
alert("数据已接收...");
};
之前也说过,websocket为双向通信,有发即有收
onmessage
函数为接受数据,在函数中可传入evt
行参来接受数据
为什么要说websocket关闭函数?因为只有在触发websocket关闭的时候才会触发的函数(是不是有点废话了)
为什么这么说呢?关闭可能存在很多种情况,后端关闭,前端关闭,通信关闭都会触发
ws.onclose = function()
{
// 关闭 websocket
alert("连接已关闭...");
};
这个应该在开始说的,一般现代浏览器都支持websocket,在MDN上面也有兼容图,我就直接贴在这里了
当然你也可以用代码的形式来检验你的浏览器是否支持websocket
if ("WebSocket" in window)
{
alert("您的浏览器支持 WebSocket!");
}else {
alert("您的浏览器不支持 WebSocket!")
}
当然,你也可以看出来,如果说浏览器支持websocket,即浏览器window对象中含有websocket对象
你可以用以下代码来进行websocket的自测
可以打开调试工具来监听websocket
注意:此代码的自测网站会将你发送的websocket请求全部以websocket形式发送给你,所以,发送我写了循环9999次,所以接受也会有9999条数据打印到控制台
直接贴代码:
<html>
<head>
<meta charset="utf-8">
<title>websocket测试title>
<div id="message">div>
<script type="text/javascript">
function WebSocketTest()
{
if ("WebSocket" in window)
{
alert("您的浏览器支持 WebSocket!");
// 打开一个 web socket
var ws = new WebSocket("ws://echo.websocket.org");
ws.onopen = function()
{
// Web Socket 已连接上,使用 send() 方法发送数据
for (let index = 0; index < 9999; index++) {
ws.send("发送数据"+index);
}
alert("数据发送中...");
};
ws.onmessage = function (evt)
{
var received_msg = evt.data;
// document.getElementById('message').innerHTML += received_msg + '
';
console.log(evt);
// alert("数据已接收...");
};
ws.onclose = function()
{
// 关闭 websocket
alert("连接已关闭...");
};
}
else
{
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
}
}
script>
head>
<body>
<div id="sse">
<a href="javascript:WebSocketTest()">运行 WebSocketa>
div>
body>
html>
简单的websocket已经完成,但是在实际工作中,我们使用websocket需要考虑到多方面应用
以下,借用公司大佬写的websocket封装的文件,来做详细讲解
发送数据前,需要在websocket中传入后台指定的参数并且加密传输
例如:
//websocket
const tio = {}
tio.ws = {};
/**
* {
ws_protocol wss or ws ,
ip,
port,
paramStr,加在ws url后面的请求参数,形如:name=张三&id=12
heartbeatTimeout,心跳时间 单位:毫秒
reconnInterval,重连间隔时间 单位:毫秒
binaryType 'blob' or 'arraybuffer';//arraybuffer是字节
handler 处理器
}
*/
tio.ws = function({
ws_protocol = 'ws', // 发送协议
ip = '192.168.1.10', // ip
port = '9326', // 端口号
heartbeatTimeout = 5000, // 心跳时间
reconnInterval = 1000, // 重连间隔
binaryType = 'blob', // 数据格式
paramStr, // 携带传过来的参数
handler
}) {
....
}
在此函数中,给定了函数默认值,并且paramStr为前端携带过来的参数
前端vue页面也封装了一个函数:
initIM() {
var that = this;
var nonce = randomNum(1, 10000),
signTimestamp = new Date().getTime(); // 创建随机数字字符串
var signature = sha1("XXXX" + nonce + signTimestamp); // 加密算法
that.tiows = new that.$tio.ws({
paramStr: {
appKey: "XXXX",
nonce: nonce,
signTimestamp: signTimestamp,
signature: signature,
userId: that.loginUser.id,
groups: "",
identity: "service"
},
handler: {
onopen: function(event, ws) {},
onclose: function(event, ws) {},
onerror: function(event, ws) {},
ping: function(ws) {
ws.send(JSON.stringify({ chatType: "heartbeat" })); // 心跳开始发送
},
onmessage: function(data, ws) {
that.loading = false;
console.log("接收消息", data);
// 成功接收数据后,给表格注入数据
// 将接收到的数据存储至store中,实现数据共享
that.$store.commit('common/updataMymsg',data.data)
for (let i = 0; i < that.tableData.length; i++) {
that.tableData[i].address = data.data;
}
}
}
});
that.tiows.connect();
},
与后端约定的数据(这里我用XXXX代替,不是appkey)与signTimestamp和nonce后进行加密,然后传输给后台通过验证,后台再将验证完的数据处理再返回新的数据给我
因为我们是通过websocket形式,以url地址将数据发送过去,即在封装的websocket方法中需要将其方法进行处理
this.ip = ip
this.port = port
this.url = ws_protocol + '://' + ip + ':' + port // 拼接url字符串
this.binaryType = binaryType
if (paramStr) {
this.url = addUrlParam(this.url, paramStr)
this.reconnUrl = this.url + "&"
} else {
this.reconnUrl = this.url + "?"
}
this.reconnUrl += "reconnect=true";
var addUrlParam = function(url = '', params = {}) {
if (url && JSON.stringify(params) != "{}") {
var paramArray = [];
Object.keys(params).forEach(function(key) {
var param = key + '=' + params[key]
paramArray.push(param)
});
var url2 = encodeURI(url + '?' + paramArray.join('&'));
var enurl = encodeURI(url2);
return enurl
} else {
return url;
}
}
上面代码执行操作了后将数据从url上面扒下来提取成为键值对的形式
为了确保数据的实时性和准确性,需要在websocket上面增加心跳
确保websocket在一定的时间内没收到信息再重新发送请求
this.handler = handler
this.heartbeatTimeout = heartbeatTimeout
this.reconnInterval = reconnInterval
this.lastInteractionTime = function() {
if (arguments.length == 1) {
this.lastInteractionTimeValue = arguments[0]
}
return this.lastInteractionTimeValue
}
this.heartbeatSendInterval = heartbeatTimeout / 2
上面的代码规定了心跳发送时间,而真正的发送心跳的时间将在websocket发送数据时执行
this.connect = function(isReconnect) {
var _url = this.url;
if (isReconnect) {
_url = this.reconnUrl;
}
try {
var ws = new WebSocket(_url);
this.ws = ws
} catch (err) {
console.log("错误", err)
}
ws.binaryType = this.binaryType; // 'arraybuffer'; // 'blob' or 'arraybuffer';//arraybuffer是字节
var self = this
ws.onopen = function(event) {
self.handler.onopen.call(self.handler, event, ws)
self.lastInteractionTime(new Date().getTime())
self.pingIntervalId = setInterval(function() {
self.ping(self)
}, self.heartbeatSendInterval) // 开启ws并设置心跳时间
}
ws.onmessage = function(event) {
if (event.data) {
var data = JSON.parse(event.data)
console.log(data);
if (data.code != 200) {
self.handler.onmessage.call(self.handler, data, ws)
}
if (data.code == 401) {
clearInterval(self.pingIntervalId)
}
}
self.lastInteractionTime(new Date().getTime())
}
ws.onclose = function(event) {
clearInterval(self.pingIntervalId) // clear send heartbeat task
try {
self.handler.onclose.call(self.handler, event, ws)
} catch (error) {}
//self.reconn(event)
}
ws.onerror = function(event) {
clearInterval(self.pingIntervalId)
self.handler.onerror.call(self.handler, event, ws)
}
return ws
}
this.reconn = function(event) {
var self = this
setTimeout(function() {
var ws = self.connect(true)
self.ws = ws
}, self.reconnInterval)
}
this.ping = function() {
var iv = new Date().getTime() - this.lastInteractionTime(); // 已经多久没发消息了
// 单位:秒
if ((this.heartbeatSendInterval + iv) >= this.heartbeatTimeout) {
this.handler.ping(this.ws)
}
};
this.send = function(data) {
this.ws.send(data);
};
}
这样,封装的一个websocket的方法就完成了,确保了数据传输的安全性和稳定性,虽然官方文档介绍websocket很粗略,但是数据传输确实需要考虑到很多方面
欢迎关注我的微信公众号一起学习前端知识
HTML5websocket|菜鸟教程
MDN|websocketAPI
websockt自测网站|websocket.org