<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Jack Jiang

    我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
    posts - 494, comments - 13, trackbacks - 0, articles - 1

    本文來自“糊糊糊糊糊了”的分享,原題《實時消息推送整理》,有優化和改動。

    1、寫在前面

    對Web端即時通訊技術熟悉的開發者來說,我們回顧網頁端IM的底層通信技術,從短輪詢、長輪詢,到后來的SSE以及WebSocket,使用門檻越來越低(早期的長輪詢Comet這類技術實際屬于hack手段,使用門檻并不低),技術手段越來越先進,網頁端即時通訊技術的體驗也因此越來越好。

    但上周在編輯《IM掃碼登錄技術專題》系列文章第3篇的時候忽然想到,之前的這些所謂的網頁端即時通訊“老技術”相對于當紅的WebSocket,并非毫無用武之地。就拿IM里的掃碼登錄功能來說,用短輪詢技術就非常合適,完全沒必要大炮打蚊子上WebSocket。

    所以,很多時候沒必要盲目追求新技術,相對應用場景來說適合的才是最好的。對于即時通訊網的im和消息推送這類即時通訊技術開發者來說,掌握WebSocket固然很重要,但了解短輪詢、長輪詢等這些所謂的Web端即時通訊“老技術”仍然大有裨益,這也正是整理分享本文的重要原因。

    學習交流:

    - 即時通訊/推送技術開發交流5群:215477170 [推薦]

    - 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

    - 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK

    本文同步發布于:http://www.52im.net/thread-3555-1-1.html

    2、推薦閱讀

    [1] 新手入門貼:史上最全Web端即時通訊技術原理詳解

    [2] 詳解Web端通信方式的演進:從Ajax、JSONP 到 SSE、Websocket

    [3] Web端即時通訊技術盤點:短輪詢、Comet、Websocket、SSE

    3、正文引言

    對于IM/消息推送這類即時通訊系統而言,系統的關鍵就是“實時通信”能力。

    從表面意思上來看,“實時通信”指的是:

    • 1)客戶端能隨時主動發送數據給服務端;
    • 2)當客戶端關注的內容在發生改變時,服務器能夠實時地通知客戶端。

    類比于傳統的C/S請求模型,“實時通信”時客戶端不需要主觀地發送請求去獲取自己關心的內容,而是由服務器端進行“推送”。

    注意:上面的“推送”二字打了引號,實際上現有的幾種技術實現方式中,并不是服務器端真正主動地推送,而是通過一定的手段營造了一種“實時通信”的假象。

    就目前現有的幾種技術而言,主要有以下幾類:

    • 1)客戶端輪詢:傳統意義上的短輪詢(Short Polling);
    • 2)服務器端輪詢:長輪詢(Long Polling);
    • 3)單向服務器推送:Server-Sent Events(SSE);
    • 4)全雙工通信:WebSocket。

    以下正文將針對這幾種技術方案,為你一一解惑。

    4、本文配套Demo和代碼

    為了幫助讀者更好的理解本文內容,筆者專門寫了一個較完整的Demo,Demo會以一個簡易聊天室的例子來分別通過上述的四種技術方式實現(代碼存在些許bug,主要是為了做演示用,別介意)。

    完整Demo源碼打包下載:

    請從同步鏈接附件中下載:http://www.52im.net/thread-3555-1-1.html

    Demo的運行效果(動圖):

    有興趣可以自行下載研究學習。

    5、理解短輪詢(Short Polling)

    短輪詢的實現原理:

    • 1)客戶端向服務器端發送一個請求,服務器返回數據,然后客戶端根據服務器端返回的數據進行處理;
    • 2)客戶端繼續向服務器端發送請求,繼續重復以上的步驟,如果不想給服務器端太大的壓力,一般情況下會設置一個請求的時間間隔。

    邏輯如下圖所示:

    使用短輪詢的優點:基礎不需要額外的開發成本,請求數據,解析數據,作出響應,僅此而已,然后不斷重復。

    缺點也顯而易見:

    • 1)不斷的發送和關閉請求,對服務器的壓力會比較大,因為本身開啟Http連接就是一件比較耗資源的事情;
    • 2)輪詢的時間間隔不好控制。如果要求的實時性比較高,顯然使用短輪詢會有明顯的短板,如果設置interval的間隔過長,會導致消息延遲,而如果太短,會對服務器產生壓力。

    短輪詢客戶的代碼實現(片段節選):

    var ShortPollingNotification = {

      datasInterval: null,

      subscribe: function() {

        this.datasInterval = setInterval(function() {

          Request.getDatas().then(function(res) {

            window.ChatroomDOM.renderData(res);

          });

        }, TIMEOUT);

        return this.unsubscribe;

      },

      unsubscribe: function() {

        this.datasInterval && clearInterval(this.datasInterval);

      }

    }

    PS:完整代碼,請見本文“4、本文配套Demo和代碼”一節。

    對應本文配套Demo的運行效果如下(動圖):

    下面是對應的請求,注意左下角的請求數量一直在變化:

    在上圖中,每隔1s就會發送一個請求,看起來效果還不錯,但是如果將timeout的值設置成5s,效果將大打折扣。如下圖所示。

    將timeout值設置成5s時的Demo運行效果(動圖):

    6、理解長輪詢(Long Polling)

    6.1 基本原理

    長輪詢的基本原理:

    • 1)客戶端發送一個請求,服務器會hold住這個請求;
    • 2)直到監聽的內容有改變,才會返回數據,斷開連接(或者在一定的時間內,請求還得不到返回,就會因為超時自動斷開連接);
    • 3)客戶端繼續發送請求,重復以上步驟。

    邏輯如下圖所示:

    長輪詢是基于短輪詢上的改進版本:主要是減少了客戶端發起Http連接的開銷,改成了在服務器端主動地去判斷所關心的內容是否變化。

    所以其實輪詢的本質并沒有多大變化,變化的點在于:

    • 1)對于內容變化的輪詢由客戶端改成了服務器端(客戶端會在連接中斷之后,會再次發送請求,對比短輪詢來說,大大減少了發起連接的次數);
    • 2)客戶端只會在數據改變時去作相應的改變,對比短輪詢來說,并不是全盤接收。

    6.2 代碼實現

    長輪詢客戶的代碼實現(片段節選):

    // 客戶端

    var LongPollingNotification = {

        // ....

        subscribe: function() {

          var that = this;

     

          // 設置超時時間

          Request.getV2Datas(this.getKey(),{ timeout: 10000 }).then(function(res) {

            var data = res.data;

            window.ChatroomDOM.renderData(res);

            // 成功獲取數據后會再次發送請求

            that.subscribe();

          }).catch(function(error) {

            // timeout 之后也會再次發送請求

            that.subscribe();

          });

          return this.unsubscribe;

        }

        // ....

    }

    筆者采用的是express,默認不支持hold住請求,因此用了一個express-longpoll的庫來實現。

    下面是一個原生不用庫的實現(這里只是介紹原理),整體的思路是:如果服務器端支持hold住請求的話,那么在一定的時間內會自輪詢,然后期間通過比較key值,判斷是否返回新數據。

    以下是具體思路:

    • 1)客戶端第一次會帶一個空的key值,這次會立即返回,獲取新內容,服務器端將計算出的contentKey返回給客戶端;
    • 2)然后客戶端發送第二次請求,帶上第一次返回的contentKey作為key值,然后進行下一輪的比較;
    • 3)如果兩次的key值相同,就會hold請求,進行內部輪詢,如果期間有新內容或者客戶端timeout,就會斷開連接;
    • 4)重復以上步驟。

    代碼如下:

    // 服務器端

    router.get('/v2/datas', function(req, res) {

      const key = _.get(req.query, 'key', '');

      let contentKey = chatRoom.getContentKey();

     

      while(key === contentKey) {

        sleep.sleep(5);

        contentKey = chatRoom.getContentKey();

      }

     

      const connectors = chatRoom.getConnectors();

      const messages = chatRoom.getMessages();

      res.json({

        code: 200,

        data: { connectors: connectors, messages: messages, key: contentKey },

      });

    });

    以下是用 express-longpoll的實現片段:

    // mini-chatroom/public/javascripts/server/longPolling.js

    function pushDataToClient(key, longpoll) {

      var contentKey = chatRoom.getContentKey();

     

      if(key !== contentKey) {

        var connectors = chatRoom.getConnectors();

        var messages = chatRoom.getMessages();

     

        long poll.publish(

          '/v2/datas',

          {

            code: 200,

            data: {connectors: connectors, messages: messages, key: contentKey},

          }

        );

      }

    }

     

    long poll.create("/v2/datas", function(req, res, next) {

      key = _.get(req.query, 'key', '');

      pushDataToClient(key, longpoll);

      next();

    });

     

    intervalId = setInterval(function() {

      pushDataToClient(key, longpoll);

    }, LONG_POLLING_TIMEOUT);

    PS:完整代碼,請見本文“4、本文配套Demo和代碼”一節。

    為了方便演示,我將客戶端發起請求的timeout改成了4s,注意觀察下面的截圖:

    可以看到,斷開連接的兩種方式,要么是超時,要么是請求有數據返回。

    6.3 基于iframe的長輪詢模式

    這是長輪詢技術的另一個種實現方案。

    該方案的具體的原理為:

    • 1)在頁面中嵌入一個iframe,地址指向輪詢的服務器地址,然后在父頁面中放置一個執行函數,比如execute(data);
    • 2)當服務器有內容改變時,會向iframe發送一個腳本<script>parent.execute(JSON.stringify(data))</script>;
    • 3)通過發送的腳本,主動執行父頁面中的方法,達到推送的效果。

    因不篇幅原因,在此不作深入介紹,有興趣的同學可以詳讀《新手入門貼:史上最全Web端即時通訊技術原理詳解》一文中的“3.3.2 基于iframe的數據流”一節。

    7、什么是Server-Sent Events(SSE)

    7.1 基本介紹

    從純技術的角度講:上兩節介紹的短輪詢和長輪詢技術,服務器端是無法主動給客戶端推送消息的,都是客戶端主動去請求服務器端獲取最新的數據。

    本節要介紹的SSE是一種可以主動從服務端推送消息的技術。

    SSE的本質其實就是一個HTTP的長連接,只不過它給客戶端發送的不是一次性的數據包,而是一個stream流,格式為text/event-stream。所以客戶端不會關閉連接,會一直等著服務器發過來的新的數據流,視頻播放就是這樣的例子。

    簡單來說,SSE就是:

    • 1)SSE 使用 HTTP 協議,現有的服務器軟件都支持。WebSocket 是一個獨立協議。
    • 2)SSE 屬于輕量級,使用簡單;WebSocket 協議相對復雜。
    • 3)SSE 默認支持斷線重連,WebSocket 需要自己實現。
    • 4)SSE 一般只用來傳送文本,二進制數據需要編碼后傳送,WebSocket 默認支持傳送二進制數據。
    • 5)SSE 支持自定義發送的消息類型。

    SSE的技術原理如下圖所示:

    SSE基本的使用方法,可以參看 SSE 的API文檔,地址是:https://developer.mozilla.org/en ... _server-sent_events

    目前除了IE以及低版本的瀏覽器不支持,絕大多數的現代瀏覽器都支持SSE:

    上圖來自:https://caniuse.com/?search=Server-Sent-Events

    7.2 代碼實現

    // 客戶端

    var SSENotification = {

      source: null,

      subscribe: function() {

        if('EventSource'inwindow) {

          this.source = newEventSource('/sse');

     

          this.source.addEventListener('message', function(res) {

            const d = res.data;

            window.ChatroomDOM.renderData(JSON.parse(d));

          });

        }

        return this.unsubscribe;

      },

      unsubscribe: function() {

        this.source && this.source.close();

      }

    }

     

    // 服務器端

    router.get('/sse', function(req, res) {

      const connectors = chatRoom.getConnectors();

      const messages = chatRoom.getMessages();

      const response = { code: 200, data: { connectors: connectors, messages: messages } };

     

      res.writeHead(200, {

        "Content-Type":"text/event-stream",

        "Cache-Control":"no-cache",

        "Connection":"keep-alive",

        "Access-Control-Allow-Origin": '*',

      });

     

      res.write("retry: 10000\n");

      res.write("data: "+ JSON.stringify(response) + "\n\n");

     

      var unsubscribe = Event.subscribe(function() {

        const connectors = chatRoom.getConnectors();

        const messages = chatRoom.getMessages();

        const response = { code: 200, data: { connectors: connectors, messages: messages } };

        res.write("data: "+ JSON.stringify(response) + "\n\n");

      });

     

      req.connection.addListener("close", function() {

        unsubscribe();

      }, false);

    });

    下面是控制臺的情況,注意觀察響應類型:

    詳情中注意查看請求類型,以及EventStream消息類型:

    PS:有關SSE更詳盡的資料就不在這里展開了,有興趣的同學可以詳讀《SSE技術詳解:一種全新的HTML5服務器推送事件技術》、《使用WebSocket和SSE技術實現Web端消息推送》。

    8、什么是WebSocket

    8.1 基本介紹

    PS:本小節內容引用自《Web端即時通訊實踐干貨:如何讓WebSocket斷網重連更快速?》一文的“3、快速了解WebSocket”。

    WebSocket誕生于2008年,在2011年成為國際標準,現在所有的瀏覽器都已支持(詳見《新手快速入門:WebSocket簡明教程》)。它是一種全新的應用層協議,是專門為web客戶端和服務端設計的真正的全雙工通信協議,可以類比HTTP協議來了解websocket協議。

    圖片引用自《WebSocket詳解(四):刨根問底HTTP與WebSocket的關系(上篇)

    它們的不同點:

    • 1)HTTP的協議標識符是http,WebSocket的是ws;
    • 2)HTTP請求只能由客戶端發起,服務器無法主動向客戶端推送消息,而WebSocket可以;
    • 3)HTTP請求有同源限制,不同源之間通信需要跨域,而WebSocket沒有同源限制。

    它們的相同點:

    • 1)都是應用層的通信協議;
    • 2)默認端口一樣,都是80或443;
    • 3)都可以用于瀏覽器和服務器間的通信;
    • 4)都基于TCP協議。

    兩者和TCP的關系圖:

    圖片引用自《新手快速入門:WebSocket簡明教程

    有關Http和WebSocket的關系,可以詳讀:

    WebSocket詳解(四):刨根問底HTTP與WebSocket的關系(上篇)

    WebSocket詳解(五):刨根問底HTTP與WebSocket的關系(下篇)

    有關WebSocket和Socket的關系,可以詳讀:WebSocket詳解(六):刨根問底WebSocket與Socket的關系》.

    8.2 技術特征

    WebSocket技術特征總結下就是:

    • 1)可雙向通信,設計的目的主要是為了減少傳統輪詢時http連接數量的開銷;
    • 2)建立在TCP協議之上,握手階段采用 HTTP 協議,因此握手時不容易屏蔽,能通過各種 HTTP 代理服務器;
    • 3)與HTTP兼容性良好,同樣可以使用80和443端口;
    • 4)沒有同源限制,客戶端可以與任意服務器通信;
    • 5)可以發送文本,也可以發送二進制數據;
    • 6)協議標識符是ws(如果加密,則為wss),服務器網址就是 URL.

    WebSocket的技術原理如下圖所示:

    關于WebSocket API方面的知識,這里不再作講解,可以自己查閱:https://developer.mozilla.org/en-US/docs/Web/API/WebSocket

    8.3 瀏覽器兼容性

    WebSocket兼容性良好,基本支持所有現代瀏覽器。

    上圖來自:https://caniuse.com/mdn-api_websocket

    8.4 代碼實現

    筆者這里采用的是socket.io,是基于WebSocket的封裝,提供了客戶端以及服務器端的支持。

    // 客戶端

    var WebsocketNotification = {

      // ...

      subscribe: function(args) {

        var connector = args[1];

        this.socket = io();

        this.socket.emit('register', connector);

        this.socket.on('register done', function() {

          window.ChatroomDOM.renderAfterRegister();

        });

        this.socket.on('data', function(res) {

          window.ChatroomDOM.renderData(res);

        });

        this.socket.on('disconnect', function() {

          window.ChatroomDOM.renderAfterLogout();

        });

      }

      // ...

    }

     

    // 服務器端

    var io = socketIo(httpServer);

    io.on('connection', (socket) => {

      socket.on('register', function(connector) {

        chatRoom.onConnect(connector);

        io.emit('register done');

        var data = chatRoom.getDatas();

        io.emit('data', { data });

      });

      socket.on('chat', function(message) {

        chatRoom.receive(message);

        var data = chatRoom.getDatas();

        io.emit('data', { data });

      });

    });

    PS:完整代碼,請見本文“4、本文配套Demo和代碼”一節。

    響應格式如下:

    8.5 深入學習

    隨著HTML5的普及率越來越高,WebSocket的應用也越來越普及,關于WebSocket的學習資料網上很容易找到,限于篇幅本文就不深入展開這個話題。

    如果想進一步深入學習WebSocket的方方面面,以下文章值得一讀:

    新手快速入門:WebSocket簡明教程

    WebSocket詳解(一):初步認識WebSocket技術

    WebSocket詳解(二):技術原理、代碼演示和應用案例

    WebSocket詳解(三):深入WebSocket通信協議細節

    WebSocket詳解(四):刨根問底HTTP與WebSocket的關系(上篇)

    WebSocket詳解(五):刨根問底HTTP與WebSocket的關系(下篇)

    WebSocket詳解(六):刨根問底WebSocket與Socket的關系

    理論聯系實際:從零理解WebSocket的通信原理、協議格式、安全性

    微信小程序中如何使用WebSocket實現長連接(含完整源碼)

    八問WebSocket協議:為你快速解答WebSocket熱門疑問

    Web端即時通訊實踐干貨:如何讓你的WebSocket斷網重連更快速?

    WebSocket從入門到精通,半小時就夠!

    WebSocket硬核入門:200行代碼,教你徒手擼一個WebSocket服務器

    長連接網關技術專題(四):愛奇藝WebSocket實時推送網關技術實踐

    9、本文小結

    短輪詢、長輪詢實現成本相對比較簡單,適用于一些實時性要求不高的消息推送,在實時性要求高的場景下,會存在延遲以及會給服務器帶來更大的壓力。

    SSE只能是服務器端推送消息,因此對于不需要雙向通信的項目比較適用。

    WebSocket目前而言實現成本相對較低,適合于雙工通信,對于多人在線,要求實時性較高的項目比較實用。

    本文已同步發布于“即時通訊技術圈”公眾號。

    ▲ 本文在公眾號上的鏈接是:點此進入。同步發布鏈接是:http://www.52im.net/thread-3555-1-1.html



    作者:Jack Jiang (點擊作者姓名進入Github)
    出處:http://www.52im.net/space-uid-1.html
    交流:歡迎加入即時通訊開發交流群 215891622
    討論:http://www.52im.net/
    Jack Jiang同時是【原創Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
    本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
    主站蜘蛛池模板: 亚洲AV无码AV日韩AV网站| 久久精品国产亚洲av麻| 亚洲欧洲日产国码高潮αv| 免费一级肉体全黄毛片| 中文字幕亚洲综合久久菠萝蜜| 国产成人亚洲综合无码精品| 亚洲资源在线视频| 亚洲男人的天堂网站| 成人a毛片视频免费看| 国产成人免费ā片在线观看老同学 | 国产zzjjzzjj视频全免费| 亚洲精品无码成人片在线观看| 亚洲爆乳无码专区| 亚洲综合精品成人| WWW免费视频在线观看播放 | 西西人体免费视频| 黄页网站在线看免费| 婷婷亚洲天堂影院| 亚洲av丰满熟妇在线播放| 在线观看日本亚洲一区| 亚洲黄片手机免费观看| 97公开免费视频| 国产免费69成人精品视频| 亚洲AV综合色区无码一区| 亚洲综合激情五月色一区| 中文字幕免费在线视频| 国产精品成人免费视频网站京东| 亚洲中文无韩国r级电影| 亚洲午夜在线一区| caoporn国产精品免费| 妻子5免费完整高清电视| 亚洲精品无码成人片在线观看 | 福利免费在线观看| 在线观看的免费网站| 亚洲中久无码永久在线观看同| 久久亚洲精品专区蓝色区| 丝袜捆绑调教视频免费区| 日韩免费毛片视频| 亚洲视频免费观看| 国产免费福利体检区久久| 成人激情免费视频|