1. 程式人生 > >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