websocket的简单介绍
-
websocket是一种网络通信协议,一般用来进行实时通信会使用到(聊天室)
-
websocket 协议和http协议类似,http协议有一个缺陷,只能由客户端发送请求,服务端根据请求url和请求的参数返回对应的结果
-
websocket是双向通信的,只要websocket连接建立起来,可以有客户端给服务器发送数据,也可以有服务器主动发送给客户端数据。
-
*注:协议标识符是ws 如果是加密的话就是wss服务器网址就是URL
前端实现
- 前端实现比较简单,有两种方式原生websocket构造函数 或者插件 vue-websocket-io库
var websocket = new WebSocket("ws://localhost:8001/");
前端代码
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>client</title>
</head>
<body>
<h1>聊天室</h1>
<input id="sendTxt"></input>
<button id="sendBtn">发送</button>
<script type="text/javascript">
var websocket = new WebSocket("ws://localhost:8001/");
function showMessage (txt,type) {
var div = document.createElement("div");
div.innerHTML = txt;
if(type=="in"){
div.style.color = "green";
}else if(type=="out"){
div.style.color = "grey";
}
document.body.appendChild(div);
}
websocket.onopen = function () {
console.log('websocket open');
document.getElementById("sendBtn").onclick = function () {
var txt = document.getElementById("sendTxt").value;
if(txt){
websocket.send(txt);
}
}
}
websocket.onclose = function () {
console.log('websocket close');
}
websocket.onmessage = function (e) {
console.log(e.data);
var mes = JSON.parse(e.data);
showMessage(mes.data,mes.type);
}
</script>
</body>
</html>
有几个钩子函数
websocket.onopen 连接成功的回调
websocket.onclose 连接断开的回调
websocket.onmessage 接收到后台传递的信息回调,可以拿到后台的参数
调用方法
websocket.send(‘传递给后台的参数’); 可以传递给后台string ,二进制数据(blob对象或Arraybuffer对象)。
后端的实现
源代码
serve.js
var ws = require("nodejs-websocket") // 需要在npm中下载
var PORT = 8001
var clientCount = 0
var server = ws.createServer(function (conn) {
console.log("New connection")
clientCount++
conn.nickname = '用户' + clientCount
var mes = {}
mes.type = "in"
mes.data = "------" + conn.nickname + '已上线'+ "------"
// console.log(conn)
broadcast(JSON.stringify(mes))
console.log('11111', server.connections)
conn.on("text",function (str) {
console.log("Received " + str)
mes.type = "text"
mes.data = conn.nickname + " : " + str
broadcast(JSON.stringify(mes))
})
conn.on("close",function (code,reason) {
console.log('Connection closed')
mes.type = "out"
mes.data = "------" + conn.nickname + '已下线'+ "------"
broadcast(JSON.stringify(mes))
})
conn.on("error",function (err) {
console.log('handle err')
console.log(err)
})
}).listen(PORT)
console.log('websocket server listening on port ' + PORT)
function broadcast (str) {
server.connections.forEach(function (connection) {
connection.sendText(str)
})
}
代码可以直接copy运行的
node serve.js
就可以查看页面效果
第六行的server 表示所有的用户连接用户 server.connections里面存放的是数组,每一个连接的socket用户对象
第六行的函数中的conn参数可以获取到当前连接到的用户对象
Event: 'close(code, reason)': 连接关闭时触发
Event: 'error(err)':发生错误时触发,如果握手无效,也会发出响应
Event: 'text(str)':收到文本时触发,str 时收到的文本字符串
Event: 'binary(inStream)':收到二进制内容时触发,inStream时一个ReadableStream
websocketIO插件
- 要是在vue里面有vue-websocket-io插件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>client</title>
<script type="text/javascript" src="socket.io.js"></script>
</head>
<body>
<h1>聊天室</h1>
<input id="sendTxt"></input>
<button id="sendBtn">发送</button>
<script type="text/javascript">
var socket = io("ws://localhost:8001/");
function showMessage (txt,type) {
var div = document.createElement("div");
div.innerHTML = txt;
if(type=="enter"){
div.style.color = "green";
}else if(type=="leave"){
div.style.color = "grey";
}
document.body.appendChild(div);
}
console.log('websocket open');
document.getElementById("sendBtn").onclick = function () {
var txt = document.getElementById("sendTxt").value;
if(txt){
socket.emit("message",txt);//socket.emit发送给单个人,io.emit广播
}
}
socket.on('enter',function (data) {
showMessage(data,'enter');
})
socket.on('message',function (data) {
showMessage(data,'message');
})
socket.on('leave',function (data) {
showMessage(data,'leave');
})
</script>
</body>
</html>
var socket = io("ws://localhost:8001/");
创建一个socket对象
socket可以监听enter事件,其实enter的事件名是与后台定义的
前端定义enter方法,后端触发前端定义的enter事件 连接成功触发
message,和leave也是前端定义的,后端触发
message事件是接收后台的数据
leave事件后台断开的时候触发前端定义的leave事件,前端收到信息
socket.emit("message",txt); 触发后台定义的message事件给后台发送数据
socket-io后台
var app = require("http").createServer()
var io = require("socket.io")(app) // npm下载
var PORT = 8001
var clientCount = 0
app.listen(PORT, () => {
console.log("聊天室开了")
})
io.on('connection',function (socket) {
clientCount++
socket.nickname = '用户' + clientCount
io.emit('enter',"------" + socket.nickname + '已上线'+ "------")
socket.on('message',function (str) {
io.emit('message',socket.nickname + " : " + str)
})
socket.on('disconnect',function () {
io.emit('leave11',"------" + socket.nickname + '已下线'+ "------")
})
})
console.log('websocket server listening on port ' + PORT)
require("socket.io")(app) 将app里面注入socket
io 是后台所有的获取所有连接到socket的信息
io.on('connection',function (socket) {})
当用户连接的时候会触发connection事件 socket参数是当前连接信息
手动触发前端定义的enter事件,给前端发现连接成功的信息
socket.on('message',function (str) {})
后端定义事件message 前端想要给后台发送信息
socket.on('disconnect',function () {})
后端定义的当连接断开的时候触发,在触发前端定义接受的事件
scoket.io.js
心跳检测
- 为什么会有心跳检测
- 为了证明客户端和服务器还在连接中,如果遭到网络问题等情况,服务端没有触发onclose事件, 这样会产生多余的连接,并且服务端会继续发送消息给客户端,造成数据丢失,因此需要一种机制来检测客户端和服务端是否处于正常连接状态,心跳检测和重连截止就产生了。
- 如何进行心跳检测和重连
- 思路是:
- 每隔一段指定的时间(计时器),向服务器发送一个数据,服务器收到数据后再发送给客户端,正常情况下客户端通过onmessage事件是能监听到服务器返回的数据的,说明请求正常。
- 如果再这个指定时间内,客户端没有收到服务器端返回的响应消息,就判定连接断开了,使用websocket.close关闭连接。 这个关闭连接的动作可以通过onclose事件监听到,因此在 onclose 事件内,我们可以调用reconnect事件进行重连操作。
具体代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>client</title>
</head>
<body>
<h1>聊天室</h1>
<input id="sendTxt"></input>
<button id="sendBtn">发送</button>
<script type="text/javascript">
function showMessage (txt,type) {
var div = document.createElement("div");
div.innerHTML = txt;
if(type=="in"){
div.style.color = "green";
}else if(type=="out"){
div.style.color = "grey";
}
document.body.appendChild(div);
}
createWebSocket()
/**
* websocket启动
*/
function createWebSocket() {
try {
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8001/");
} else if ('MozWebSocket' in window) {
websocket = new MozWebSocket("ws://localhost:8001/");
} else {
websocket = new SockJS("ws://localhost:8001/");
}
init();
} catch (e) {
console.log('catch' + e);
reconnect();
}
}
function init() {
//连接成功建立的回调方法
websocket.onopen = function (event) {
console.log("WebSocket:已连接");
//心跳检测重置
document.getElementById("sendBtn").onclick = function () {
var txt = document.getElementById("sendTxt").value;
if(txt){
websocket.send(txt);
}
}
heartCheck.reset().start();
};
//接收到消息的回调方法
websocket.onmessage = function (event) {
// showNotify(event.data); 正式要处理的逻辑
console.log("WebSocket:收到一条消息", event.data);
var mes = JSON.parse(event.data);
showMessage(mes.data,mes.type);
heartCheck.reset().start();
};
//连接发生错误的回调方法
websocket.onerror = function (event) {
console.log("WebSocket:发生错误");
reconnect();
};
//连接关闭的回调方法
websocket.onclose = function (event) {
console.log("WebSocket:已关闭");
heartCheck.reset();//心跳检测
reconnect();
};
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
websocket.close();
};
//关闭连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function send(message) {
websocket.send(message);
}
}
//避免重复连接
var lockReconnect = false, timeID;
/**
* websocket重连
*/
function reconnect() {
if (lockReconnect) {
return;
}
lockReconnect = true;
timeID && clearTimeout(timeID);
timeID = setTimeout(function () {
console.log('重连中...');
lockReconnect = false;
createWebSocket();
}, 4000);
}
/**
* websocket心跳检测
*/
var heartCheck = {
timeout: 5000,
timeoutObj: null,
serverTimeoutObj: null,
reset: function () {
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start: function () {
var self = this;
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(function () {
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
websocket.send("HeartBeat");
console.log('ping');
self.serverTimeoutObj = setTimeout(function () { // 如果超过一定时间还没重置,说明后端主动断开了
console.log('关闭服务');
websocket.close();//如果onclose会执行reconnect,我们执行 websocket.close()就行了.如果直接执行 reconnect 会触发onclose导致重连两次
}, self.timeout)
}, this.timeout)
}
};
</script>
</body>
</html>