WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
为什么需要 WebSocket ?
初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?
答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。
这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。
轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。
随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据。
我们知道,传统的HTTP协议是无状态的,每次请求(request)都要由客户端(如 浏览器)主动发起,服务端进行处理后返回response结果,而服务端很难主动向客户端发送数据;这种客户端是主动方,服务端是被动方的传统Web模式 对于信息变化不频繁的Web应用来说造成的麻烦较小,而对于涉及实时信息的Web应用却带来了很大的不便,如带有即时通信、实时数据、订阅推送等功能的应用。
//先握手,并查看客户端的浏览器是否支持websocket协议
//连接发生错误的回调方法
ws.onerror = function () {
alert("WebSocket连接发生错误");
};
//连接成功建立的回调方法
ws.onopen = function () {
alert("WebSocket连接成功");
}
//接收到消息的回调方法,websocket的controller里的onMessage方法session_to传过来的值
ws.onmessage = function (event) {
alert(event.data);
}
//连接关闭的回调方法
ws.onclose = function () {
alert("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
}
function closeWebSocket(){
ws.close();
}
let ws = require('ws'); //引入websocket模块
let uuid = require('uuid'); //引入创建唯一id模块
let socketServer = ws.Server;
let clientIndex = 0;
let wss = new socketServer({port: 8090}, ()=>{
console.log("服务启动,端口8090.")
}); //创建websocketServer实例监听8090端口
let clients = []; //创建客户端列表,用于保存客户端及相关连接信息
/**
* 广播所有客户端消息
* @param {String} type 广播方式(admin为系统消息,user为用户消息)
* @param {String} message 消息
* @param {String} nickname 用户昵称,广播方式为admin时可以不存在
*/
function broadcastSend(type, message, nickname) {
console.log("type:"+type)
clients.forEach(function(v, i) {
if(v.ws.readyState === ws.OPEN) {
v.ws.send(JSON.stringify({
"type": type,
"nickname": nickname,
"message": message
}));
}
})
}
//定时发送图表数据
function echartData() {
function sortNumber(a,b){
return a-b;//升序
}
//测试数据
var iArray = [];
function getRamdon(start, end) {
var temp = end - start + 1; //91
return Math.abs(Math.floor(Math.random()*temp)) + start; //从start开始
}
for(var i=0; i < 10; i++) {
iArray.push(getRamdon(100,1000)) //循环10次,添加10个数在数组里
}
iArray.sort(sortNumber);
return iArray;
}
var inter = null;
function echartSend() {
//第一次,以后每5秒更新一次
var arr = echartData();
broadcastSend("echart", arr, "system");
if (inter) clearInterval(inter);
inter = setInterval(function() {
var arr = echartData();
broadcastSend("echart", arr, "system");
}, 5000)
}
//监听连接
wss.on('connection', function(ws) {
let client_uuid = uuid.v4();
let nickname = `AnonymousUser${clientIndex++}`;
clients.push({
"id": client_uuid,
"ws": ws,
"nickname": nickname
});
//发送图表数据
echartSend();
console.log(`client ${client_uuid} connected`);
/**
* 关闭服务,从客户端监听列表删除
*/
function closeSocket() {
for(let i = 0; i < clients.length; i++) {
if(clients[i].id == client_uuid) {
let disconnect_message = `${nickname} has disconnected`;
broadcastSend("notification", disconnect_message, nickname);
clients.splice(i, 1);
}
}
//无客户端时停止定时器
if (clients.length == 0){
console.log("无客户端连接,清除定时器。")
if (inter) clearInterval(inter);
}
}
/*监听消息*/
ws.on('message', function(message) {
if(message.indexOf('/nick') === 0) {
let nickname_array = message.split('_');
if(nickname_array.length >= 2) {
let old_nickname = nickname;
nickname = nickname_array[1];
let nickname_message = `Client ${old_nickname} change to ${nickname}`;
broadcastSend("nick_update", nickname_message, nickname);
}
} else {
broadcastSend("message", message, nickname);
}
});
/*监听断开连接*/
ws.on('close', function() {
closeSocket();
})
})
【其他代码可参考】https://gitee.com/monkeyhlj/vue-learning/tree/master/20210809-WebSocket%E5%AD%A6%E4%B9%A0
【Java实现可参考】Java实现websocket