SockJS實踐:即時通信關鍵點

如果說之前的 Socket.IO打造基礎聊天室 讓我明白了聊天室的原理,知道了如何實現羣聊(廣播)和私聊(單播)等,那麼對於 SockJS 的實踐讓我更加了解了websocket,因爲 Sock.IO 是自己封裝的接口,而 SockJS 則使用了跟 websocket 幾乎相同的 API。

01 SockJS簡介

  • 與 Socket.io 不同,在傳輸方式的選擇方面,SockJS 會優先採用 websocket,然後再自動降級;
  • 其 API 幾乎與 websocket API 的使用方式相同;
  • 兼容跨瀏覽器,支持跨域。

02 即時通信關鍵點

(1)心跳檢測(業務層面)

(ws自身有心跳,但是如果有一些業務層面的需求,就需要自己實現心跳)

ws建立成功時便進行 心跳請求(每隔一段時間發送一個 PING),同時初始化 超時重連。如果在達到心跳規定次數後仍沒有返回 PONG,則判定心跳超時,前端主動關閉ws,觸發 ws重連

如果期間收到了 PONG,則重新初始化超時重連。

【注意】:ws重連的時候,要清空之前的心跳定時器。

(2)關閉連接的3種情況:

  • 心跳超時(連接層不斷)的情況,則前端可主動關閉 ws;
  • 連接未建立成功(如TCP連接斷掉),ws 自動關閉;
  • 服務端關閉(如多端剔除),這是要防止前端進行重連。

正常關閉(如心跳超時)

連接失敗(如TCP層沒有連接成功)

【注意】:鎖屏情況下,js 會停止工作,這時,ws 會自動關閉,當屏幕喚醒時,通過觸發 onclose 事件,ws 又會進行重連。在某些特殊業務場景下,需要注意下這種情況。

(3)websocket 自動重連

無論是前端主動關閉 ws,還是ws自動關閉,都會觸發 onclose 事件,可在其中進行重連。如果達到了重連次數或者後端返回了不可進行重連的標誌碼,則不進行重連。

【實現思路】:

var CONNECT_COUNT = 3;
var sockjs;
var sockjs_url = '/chat/sjs/';
var isClose = false;
var connectCount = 1;

new_conn = function() {    
    sockjs = new SockJS(sockjs_url);

    sockjs.onopen = function() {
        connectCount = 1;   // 重置重連次數
        this.checkHeartBeat();  // 心跳檢測
    }

    sockjs.onmessage = function(e) {
        var data = JSON.parse(e.data);
        // handleMessage(data);
    };
    
    sockjs.onclose = function(e) {
        // 已經關閉的情況,不重連
        if (isClose) { 
            return;
        }
        
        // 小於重連次數
        if (connectCount < CONNECT_COUNT) {
            setTimeout(function() {
                new_conn();
            }, (Math.random() * 3 + 1 )* 1000);   
        } else {
            isClose = true;
        }
    };
}

(4)sockJS session_id:可用於多端剔除

url格式類似於:/resource/<server_number>/<session_id>/transport

請求URL

每個 sockJS 都有 session_id,可通過它來判斷是否同一個用戶建立了2個ws,如多端登錄的情況下,可用於進行多端剔除。

var socket = new SockJS('/mqClient');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
        console.log(socket._transport.url); 
        //it contains ws://localhost:8080/mqClient/039/byxby3jv/websocket
        //sessionId is byxby3jv
    });

The SockJS constructor has an option parameter and there you can pass a custom session id generator as a function:

let sessionId = utils.random_string(8);
let socket = new SockJS('/socket', [], {
    sessionId: () => {
       return sessionId
    }
 });

 

03 sockJS readyState(暫時沒用到。。)

【sockjs-client 部分源碼】:

SockJS.CONNECTING = 0;
SockJS.OPEN = 1;
SockJS.CLOSING = 2;
SockJS.CLOSED = 3;
SockJS.prototype.close = function(code, reason) {
  // Step 1
  if (code && !userSetCode(code)) {
    throw new Error('InvalidAccessError: Invalid code');
  }
  // Step 2.4 states the max is 123 bytes, but we are just checking length
  if (reason && reason.length > 123) {
    throw new SyntaxError('reason argument has an invalid length');
  }

  // Step 3.1
  if (this.readyState === SockJS.CLOSING || this.readyState === SockJS.CLOSED) {
    return;
  }

  // TODO look at docs to determine how to set this
  var wasClean = true;
  this._close(code || 1000, reason || 'Normal closure', wasClean);
};

參考

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章