此jq封装方法来自于这里
代码简单易懂,如下:
//jqwebsocket.js
//==========websocket
(function($) {
$.websocket = function(options) {
var defaults = {
domain: top.location.hostname,
port:3398,
protocol:""
};
var opts = $.extend(defaults,options);
var szServer = "ws://" + opts.domain + ":" + opts.port + "/" + opts.protocol;
var socket = null;
var bOpen = false;
var t1 = 0;
var t2 = 0;
var messageevent = {
onInit:function(){
if(!("WebSocket" in window) && !("MozWebSocket" in window)){
return false;
}
if(("MozWebSocket" in window)){
socket = new MozWebSocket(szServer);
}else{
socket = new WebSocket(szServer);
}
if(opts.onInit){
opts.onInit();
}
},
onOpen:function(event){
bOpen = true;
if(opts.onOpen){
opts.onOpen(event);
}
},
onSend:function(msg){
t1 = new Date().getTime();
if(opts.onSend){
opts.onSend(msg);
}
socket.send(msg);
},
onMessage:function(msg){
t2 = new Date().getTime();
if(opts.onMessage){
opts.onMessage(msg.data,t2 - t1);
}
},
onError:function(event){
if(opts.onError){
opts.onError(event);
}
},
onClose:function(event){
if(opts.onclose){
opts.onclose(event);
}
if(socket.close() != null){
socket = null;
}
}
}
messageevent.onInit();
socket.onopen = messageevent.onOpen;
socket.onmessage = messageevent.onMessage;
socket.onerror = messageevent.onError;
socket.onclose = messageevent.onClose;
this.send = function(pData){
if(bOpen == false){
return false;
}
messageevent.onSend(pData);
return true;
}
this.close = function(){
messageevent.onClose();
}
return this;
};
})(jQuery);
封装成jQuery主要是为了使代码更加的简洁,易懂,十分合适新手阅读,使用方法如下:(使用时必须引入上面的代码,jqwebsocket.js)
//=========启动一个websocket
var Socket1 = $.websocket({
domain:"www.qhnovel.com", //这是与服务器的域名或IP
port:8080, //这是服务器端口号
protocol:"text", //这东西可有可无,组合起来就是 ws://www.qhnovel.com:8080/test
//这个是连接后台服务器成功后,回调的函数
onOpen:function(event){
console.log("已经与服务端握手,onOpen可省略不写");
},
//这个是连接后台服务器失败后,回调的函数
onError:function(event){
console.log("发生了错误,onError可省略不写");
},
//这个是Socket1.send()发送成功后,回调的函数
onSend:function(msg){
console.log("发送数据额外的代码,可省略不写");
},
//这个是收到后台放回的数据后的回调函数
onMessage:function(result,nTime){
console.log("从服务端收到的数据:" + result);
console.log("最近一次发送数据到现在接收一共使用时间:" + nTime);
}
});
//=========发送数据方式
Socket1.send("要发送的数据");
//=========关闭连接
Socket1.close();
到这来,聊天室前端的websocket的基本骨架完成了。能够通过WebSocket来发送字符串了,如果需要发送文件、图片其他的,可以在onSend中使用http等其他协议跨平台请求。
对于WebSocket 来说,它必须依赖 HTTP 协议进行一次握手 ,握手成功后,数据就直接从 TCP 通道传输。而平常的post表单提交都是http协议的,说这个是为心跳机制做铺垫,基于tcp连接消耗小,而http的话,消耗较大,所以我们一般使用tcp来发送心跳,就是ws.send(‘ping’);
聊天室重连机制、心跳机制代码如下:
1、对插件的一些小修改,我是为了web移动端的浏览器切换后台或熄屏后,有重新切换到聊天室的重连处理
//==========websocket
(function($) {
$.websocket = function(options) {
var defaults = {
domain: top.location.hostname,
port:3398,
protocol:""
};
var opts = $.extend(defaults,options);
var szServer = "ws://" + opts.domain + ":" + opts.port + "/" + opts.protocol;
var socket = null;
var bOpen = false;
var t1 = 0;
var t2 = 0;
var messageevent = {
onInit:function(){
if(!("WebSocket" in window) && !("MozWebSocket" in window)){
return false;
}
if(("MozWebSocket" in window)){
socket = new MozWebSocket(szServer);
}else{
socket = new WebSocket(szServer);
}
if(opts.onInit){
opts.onInit();
}
},
onOpen:function(event){
bOpen = true;
if(opts.onOpen){
opts.onOpen(event);
}
},
onSend:function(msg){
t1 = new Date().getTime();
if(opts.onSend){
opts.onSend(msg);
}
socket.send(msg);
},
onMessage:function(msg){
t2 = new Date().getTime();
if(opts.onMessage){
opts.onMessage(msg.data,t2 - t1);
}
},
onError:function(event){
if(opts.onError){
opts.onError(event);
}
},
onClose:function(event){
if(opts.onclose){
opts.onclose(event);
}
if(socket.close() != null){
socket = null;
}
//websocket关闭的时候,判断是否浏览器的情况
if(document.visibilityState=='hidden') {
//页面被隐藏或调到后台的情况,我们这就不做处理
}else{
//页面还在时异常被关闭才进行重连
console.log("Close回调函数,自动重连")
reconnect();
}
}
}
messageevent.onInit();
socket.onopen = messageevent.onOpen;
socket.onmessage = messageevent.onMessage;
socket.onerror = messageevent.onError;
socket.onclose = messageevent.onClose;
this.send = function(pData){
if(bOpen == false){
return false;
}
messageevent.onSend(pData);
return true;
}
this.close = function(){
messageevent.onClose();
}
return this;
};
})(jQuery);
2、重连+心跳 机制
//=========启动一个websocket
var Socket1;
Socket1 = $.websocket({
domain:"www.qhnovel.com", //这是与服务器的域名或IP
port:8080, //这是服务器端口号
protocol:"text", //这东西可有可无,组合起来就是 ws://www.qhnovel.com:8080/test
onOpen:function(event){
alert("已经与服务端握手,onOpen可省略不写");
heartCheck.reset().start(); //传递心跳信息
},
onError:function(event){
alert("发生了错误,onError可省略不写");
reconnect();//断开重连
},
onSend:function(msg){
alert("发送数据额外的代码,可省略不写");
if(sen_flag == 2){
//已经ping过去了
console.log('ping');
} else{
//用来处理正常的聊天室消息
}
},
onMessage:function(result,nTime){
//我这用result == 'pong'有个大大大的前提,就是ping给后台的时候,后台是直接推送pong回来给我的,所以我这里可以直接的判断result == 'pong'
if(result == 'pong'){
console.log('pong');
//如果获取到消息,心跳检测重置
//拿到任何消息都说明当前连接是正常的
heartCheck.reset().start();
}
alert("从服务端收到的数据:" + result);
alert("最近一次发送数据到现在接收一共使用时间:" + nTime);
}
});
//这个是我在具体项目中用来辨别心跳和正常消息的标志位
var sen_flag = 1;
//避免重复连接
var lockReconnect = false;
// 重连函数
function reconnect(){
console.log("websocket正在重新连接")
if (lockReconnect) return;
lockReconnect = true; //没连接上会一直重连,设置延迟避免请求过多
setTimeout(function() {
getwebsocket();
lockReconnect = false;
}, 5000);
}
//心跳检测
var heartCheck = {
timeout: 60000, //60秒
timeoutObj: null,
serverTimeoutObj: null,
reset: function() {
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start: function() {
var self = this;
this.timeoutObj = setTimeout(function() {
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
sen_flag = 2;
Socket1.send("ping");
sen_flag = 1;
self.serverTimeoutObj = setTimeout(function() { //如果超过一定时间还没重置,说明后端主动断开了
console.log('未收到pong,后端已断开');
Socket1.close();
//如果onclose会执行reconnect我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
}, self.timeout)
}, this.timeout)
}
}
//当visibilityState流浪器的状态改变时,进行监听
document.addEventListener('visibilitychange',function() {
if(document.visibilityState=='hidden') {
//页面被隐藏或调到后台
}else{
//页面还在时异常被关闭才进行重连
console.log("Close回调函数,自动重连")
reconnect();
}
})
参考的文章有:https://blog.csdn.net/weixin_42790916/article/details/89640891
https://blog.csdn.net/z_demon801/article/details/85097059
若有更好的写法,欢迎分享。