webSocket如何解决自动关闭的意思

我的前一篇文章“webSocket如何在自己的工程中使用?

地址:https://blog.csdn.net/jintingbo/article/details/80755636

讲述了webSocket的初级使用,初学者可以先看看那篇文章。

本文主要是解决webSocket自动关闭。

websocket它有一个“心跳”机制,但这个心跳机制是要程序自己去写代码实现的,websocket本身没有给你做这个东西。

它是如何自动关闭的呢?当电脑浏览器发送pong帧的时候,由于内容为空,于是服务器将空内容转发回去,导致客户端浏览器以为是错误的帧类型,发送关闭信息进行error关闭。(服务器返回时,必须要把它原来发来的东西发回去的,这是TCP/IP协议的要求)。

即然服务器接收到浏览器发送的"pong"后如果回复一个“pong”时,它会结束连接,那么为了避免这种事发生,可以在服务器接收到一个“pong”空信息时,不回复它的"pong",也就不关闭连接了。这是一种解决方案。

另一种解决方案是:当发生关闭时,可以主动发送心跳给对方,让连接复活,这样就相当于不断线了。本文就是用这个方案实现的。

第一步:在html文件中加入:
-----------------------------

<script type="text/javascript">
var ws;
//避免重复连接
var lockReconnect = false;
var wsUrl = "ws://localhost:8080/cl-market-camera-web/websocket";
createWebSocket(wsUrl);
function createWebSocket(url) {
try {
ws = new WebSocket(url);
initEventHandle();
} catch (e) {
                        //重新连接
reconnect(url);
}
}
        //封装websocket的那几个接口函数
function initEventHandle() {
ws.onclose = function () {
console.info("连接关闭");
reconnect(wsUrl);
};
ws.onerror = function () {
console.info("传输异常");
reconnect(wsUrl);
};
ws.onopen = function () {
//心跳检测重置
heartCheck.reset().start();
};

websocket.onmessage = function(event) {
                    //console.info(event.data);

                    setMessageInnerHTML(event.data);
                    //如果获取到消息,心跳检测重置
                    heartCheck.reset().start();
}
}
function reconnect(url) {
if(lockReconnect) return;
lockReconnect = true;
//没连接上会一直重连,设置延迟避免请求过多
setTimeout(function () {
    console.info("尝试重连..." + new Date().format("yyyy-MM-dd hh:mm:ss"));
    createWebSocket(url);
    lockReconnect = false;
}, 5000);
}
//心跳检测,每5s心跳一次
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 = setTimeout(function(){
        //这里发送一个心跳,后端收到后,返回一个心跳消息,
        //onmessage拿到返回的心跳就说明连接正常
        ws.send("HeartBeat" + new Date().format("yyyy-MM-dd hh:mm:ss"));
                console.info("客户端发送心跳:" + new Date().format("yyyy-MM-dd hh:mm:ss"));

                self.serverTimeoutObj = setTimeout(function(){
                            //如果超过一定时间还没重置,说明后端主动断开了

                            ws.close();
                            //如果onclose会执行reconnect,我们执行ws.close()就行了.
                            //如果直接执行reconnect 会触发onclose导致重连两次
                        }, self.timeout)
                    }, this.timeout)
                }
}
//js中格式化日期,调用的时候直接:new Date().format("yyyy-MM-dd hh:mm:ss")
Date.prototype.format = function(fmt) {
var o = {
    "M+" : this.getMonth()+1,                 //月份 
    "d+" : this.getDate(),                    //日 
    "h+" : this.getHours(),                   //小时 
    "m+" : this.getMinutes(),                 //分 
    "s+" : this.getSeconds(),                 //秒 
    "q+" : Math.floor((this.getMonth()+3)/3), //季度
    "S"  : this.getMilliseconds()             //毫秒 
        }; 
        if(/(y+)/.test(fmt)) {

    fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); 

                }

        for(var k in o) {
    if(new RegExp("("+ k +")").test(fmt)){
        fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
            }
                }
        return fmt; 
        }
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
</script>

第二步:在java的controller包中写一个WebSocketTest类
------------------------------
package com.clmarket.controller;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint("/websocket")
public class WebSocketTest {
        //设置连接数
private static int onlineCount = 0;
private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = 
new CopyOnWriteArraySet<WebSocketTest>();
private Session session;
public Session getSession(){
return this.session;
}
@OnOpen
public void onOpen(Session session){
this.session = session;
webSocketSet.add(this);     //加入set中
addOnlineCount();           //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
}
@OnClose
public void onClose(){
webSocketSet.remove(this);  //从set中删除
subOnlineCount();           //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 服务器向浏览器发送消息
* @param message 需要推送到浏览器的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
for(WebSocketTest item: webSocketSet){
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}

/**
* 发生错误时调用
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException{
this.session.getBasicRemote().sendText(message);
//this.session.getAsyncRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}

public static synchronized void addOnlineCount() {
WebSocketTest.onlineCount++;
}

public static synchronized void subOnlineCount() {
WebSocketTest.onlineCount--;
}

}

第三步:如何把自己的信息借助上面的WebSocketTest类推送出去
-----------------------------

比如服务器有一个字符串“鄂H AAAAA8”这样的字符串,要把它推到浏览器里显示出来,

其实只要三句话搞定:


String license=“鄂H AAAAA8”;

//new一个WebSocketTest对象,表示我要用它来发送
WebSocketTest wst=new WebSocketTest();
//这个session实际上是import javax.websocket.Session;
Session session=wst.getSession();
//调用这个webSocketTest对象的onMessage就可以把license发送出去了。
wst.onMessage(license, session);

全文完:湖北荆门金庭波 QQ:14280784 

猜你喜欢

转载自blog.csdn.net/jintingbo/article/details/80864030