HTML5伺服器推送消息的各種解決辦法

来源:http://www.cnblogs.com/ShimizuShiori/archive/2016/05/06/5464063.html
-Advertisement-
Play Games

摘要 在各種BS架構的應用程式中,往往都希望服務端能夠主動地向客戶端推送各種消息,以達到類似於郵件、消息、待辦事項等通知。 往BS架構本身存在的問題就是,伺服器一直採用的是一問一答的機制。這就意味著如果客戶端不主動地向伺服器發送消息,伺服器就無法得知如何給客戶端推送消息。 隨著HTML、瀏覽器等各項 ...


摘要

在各種BS架構的應用程式中,往往都希望服務端能夠主動地向客戶端推送各種消息,以達到類似於郵件、消息、待辦事項等通知。

往BS架構本身存在的問題就是,伺服器一直採用的是一問一答的機制。這就意味著如果客戶端不主動地向伺服器發送消息,伺服器就無法得知如何給客戶端推送消息。

隨著HTML、瀏覽器等各項技術、標準的發展,依次生成了不同的手段與方法能夠實現服務端主動推送消息,它們分別是:AJAX,Comet,ServerSent以及WebSocket。

本篇文章將對上述提及到的各種技術手段進行直白化的解釋。


AJAX

正常的一個頁面在瀏覽器中是這樣工作的:

  1. 用戶向給予瀏覽器一個需要訪問的地址
  2. 瀏覽器根據這個地址訪問伺服器,並與伺服器之間創建一個TCP連接(HTTP請求)
  3. 伺服器根據這個地址和一些其它數據,組建一段HTML文本,將寫入TCP連接,然後關閉連接
  4. 瀏覽器得到了來自伺服器的HTML文本,解析並呈現了瀏覽器上給用戶瀏覽

此時,用戶點擊了網站上任何一個<a>或觸發任何一個<form>提交時:

  1. 瀏覽器根據form的參數或者a的參數,作為訪問的地址
  2. 與伺服器創建TCP連接
  3. 伺服器組建HTML文本,然後關閉連接
  4. 瀏覽器將當前顯示的頁面摧毀,並按照新的HTML文本呈現一個新的頁面給用戶

我們不難發現的是整個過程中間,一旦建立了一個連接,頁面就無法再維護住了。整個過程看上去有點強買強賣,也許我只要一杯新的可樂,但你非要給我一整個套餐組合。

此時我們可以瞭解一下XmlHttpRequest組件,這個組件提供我們手動創建一個HTTP請求,發送我們想要的數據,伺服器也可以只返回我們想要的結果,最大的好處是,當我們收到伺服器的響應時,原來的頁面沒有被摧毀。這就好比,我喊一句"我的咖啡喝完了,我要續杯",然後服務員就拿了一杯咖啡過來,而不是會把我沒吃完的套餐全部倒掉。

當我們利用AJAX實現伺服器推送時,其實質是客戶端不停地向伺服器詢問"有沒有給我的消息呀?",然後伺服器回答"有"或"沒有"來達到的實現效果。它的實現方法也很簡單,利用jQuery框架封裝好的AJAX調用也很方便:

function getMessage(fn) {
    $.ajax({
        url: "Handler.ashx", //一個能夠提供消息的頁面
        dataType: "text",    //響應類型,可以是JSON,XML等其它類型
        type: "get",         //HTTP請求類型,還可以是post
        success: function (d, s) {
            fn(d);           //得到了正常的響應時,利用回調函數通知外部
        },
        complete: function (x, s) {
            setTimeout(function () {
                getMessage(fn);
            }, 5000);       //無論響應成功或失敗,在若幹秒後再詢問一次伺服器
        }
    });
}

 通過上面的代碼,可以每隔5秒詢問一次伺服器是否有需要處理的消息,通過這種方式可以達到推送的效果,但是會存在一個問題:

  1. 間隔時間越快,推送的及時性越好,伺服器的消費越大;
  2. 間隔時間越慢,推送的及時性越低,伺服器的消費越小。

而且嚴格地來說,這種實際方式,並不是真正意義上的伺服器主動推送消息,但由於早期技術手段缺乏,所以AJAX輪循成為了一種很普遍的手段。

 


 

 

Comet

我們知道HTTP請求其實是基於TCP連接實現的,再看看之前說的HTTP請求處理過程:

  1. 客戶端與伺服器建立TCP連接
  2. 伺服器根據客戶端提交的報文處理並生成HTML文本
  3. 將HTML封閉成為HTTP協議報文並返回給客戶端
  4. 關閉鏈接。

看到這個處理過程,我們不難聯想到,如果把第4步——關閉連接給省掉,那不就相當於有一個長連接一直被維持住了麽。通過對服務端的一些操作,我們可以直接將數據從這個TCP連接發送客戶端了。

通過這種技術,我們可以大大提高伺服器推送的實時性,還可以減去服務端不停地建立、施放連接所形成的開銷。

目前市面上有不少基於AJAX實現的Comet機制,但主要有兩種方式:

  1. 建立連接後依然使用"詢問"+"應答"的模式。雖然工作方式沒變,但是因為減去了每次建立與施放連接的工作,所以性能上提升了很多。而且伺服器對TCP連接可以有上下文的定義,而不像以前的AJAX完全是無狀態的。
  2. 通過對Stream的寫入實現伺服器將數據主動發送到客戶端。因為是TCP連接,所以通過對伺服器的編程,我們可以主動的把數據從服務端發送給客戶端,從模式上真正建立起了推送的概念。

 


 

 

Server-Sent

Server-Sent是HTML5提出一個標準,它延用了Comet的思路,並對其進行了一些規範。使得Comet這項技術由原來的分支衍生技術轉成了正統的官方標準。

它的原理與Comet相同,由客戶端發起與伺服器之間創建TCP連接,然後並維持這個連接,至到客戶端或伺服器中的做任何一放斷開,ServerSent使用的是"問"+"答"的機制,連接創建後瀏覽器會周期性地發送消息至伺服器詢問,是否有自己的消息。

這項標準不僅要求了支持的瀏覽器能夠原生態的創建與伺服器的長連接,更要求了對JavaScript腳本的統一性,使得兼程該功能的瀏覽器可以使用同一套代碼完成Server-Sent的編碼工作。

創建代碼非常簡單:

//定義一個ServerSent對象
var s = new EventSource("Handler.ashx");
//當收到一個非自定義事件時的回調函數
s.onmessage = function (e) {
    alert(e.data);
};
//當收到一個被伺服器命名為MyEvent事件消息時的回調函數
s.addEventListener("MyEvent", function (e) {
    alert(e.data);
});

而伺服器的代碼也很簡單:

public class Handler : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/event-stream";
        context.Response.Expires = -1;
        context.Response.Write("event: MyEvent\r\n");       //事件類型,使用\r\n結尾
        context.Response.Write("data: HelloWorld!\r\n");    //事件數據,換行時使用\r\n,併在新行再加上data:
        context.Response.Write("data: I'm server!\n\n");    //事件數據結束,使用\n\n
        context.Response.Flush();                           //這裡不能用End,否則是關閉連接的
    }

    public bool IsReusable
    {
        get
        {
            return true;
        }
    }

}

兩小段代碼,就已經具備了伺服器消息推送了。

總得來說SeverSent就是HTML5規範下的Comet,具有更好的統一性,而且簡單好用。

 

 


 

 

WebSocket

看名字就知道了,這是一個可以用在瀏覽器上的Socket連接。

這也是一個HTML5標準中的一項內容,他要求瀏覽器可以通過JavaScript腳本手動創建一個TCP連接與服務端進行通訊。

WebSocket不包含太多的額外功能,僅僅就是TCP連接的幾項基本功能:建立,臨時以及發送。

另外WebSocket使用了ws和wss協議,需要伺服器有與之握手的演算法才能將連接打開。

所以WebSocket相對於之前幾種手段來說,其編碼量是最大的,但由於沒有其它的約束,因此它也可以自由地實現所有可能的功能。

即可以滿足"問"+"答"的響應機制,也可以實現主動推送的功能。

與ServerSent相同,HTML5也對WebSocket調用的JavaScript進行規範,我們可以弄過很簡單的一代碼構建一個WebSocket連接

var ws = new WebSocket("ws://192.168.0.105:10080"); //連接伺服器        

ws.onopen = function (event) { alert("已經與伺服器建立了連接\r\n當前連接狀態:" + this.readyState); };
ws.onmessage = function (event) { alert("接收到伺服器發送的數據:\r\n" + event.data); };
ws.onclose = function (event) { alert("已經與伺服器斷開連接\r\n當前連接狀態:" + this.readyState); };
ws.onerror = function (event) { alert("WebSocket異常!"); };

還可以通過send的方式發送消息

ws.send("Hello World");

WebSocket具有較為複雜的協議,需要在服務端做額外編程才能進行數據通訊。有關協議的詳細內容,我會在以後的文章中進行解釋。

 

WebSocket + MessageQueue

MessageQueue,簡稱MQ,也就是消息列隊。是一種常常用於Tcp服務端的技術。通過生產和訪問各種消息類型,MQ伺服器會將生產者所生成的消息發給感興趣的客戶端。市面上有很多的MQ框架,比如:ActiveMQ。

ActiveMQ已經支持了WebSocket協議,也就意味著,WebSocket已經可以作為一個生產者或一個消費者,與MQ伺服器連接。

開發者可以通過MQTT的JS腳本,連接上MQ伺服器,同時將Web伺服器也連上MQ伺服器,從此可以告別了Http通訊協議,完完全全使用Socket通訊來完成數據的交換。

 


 

總結:

總得來說,在HTML5規範下,最推薦使用ServerSent和WebSocket的方式進行伺服器消息的推送。

對比這兩種方式。

ServerSent的方式,可以使服務端的開發依然依用以前的方式,但是其工作方式與Comet類似。

而WebSocket的方式,則對服務端的開發有著較高的要求,但其工作方式是完全的推送。

我本人其實挺偏向WebSocket + MQ的工作方式,但是對於老項目的翻新,還是用SeverSent比較好

 


結尾

本文為作者原創,轉載請註明出處:http://www.cnblogs.com/ShimizuShiori/p/5464063.html

文章中的相關代碼可以在 http://j.zizhusoft.com/Develop/Explorer.aspx 中的ServerSent目錄中查看

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 自從學會call/apply/bind這三個方法後我就各種場合各種使用各種得心應手至今還沒踩過什麼坑,怎麼用?說直白點就是我自己的對象沒有某個方法但別人有,我就可以通過call/apply/bind去調用執行別人家的方法,不太懂具體用法的同學可移至MDN學習一下Function.prototype. ...
  • [文章系摘自網路,看到有用記錄在博客,同時共用給大家] 原鏈接:http://www.2cto.com/kf/201409/335383.html 方式1,splice var ary = [1,2,3,4]; ary.splice(0,ary.length); console.log(ary);  ...
  • 做了一個作業,用JavaScript列印正倒直線,突然覺得自己還是邏輯有待加強訓練啊 做了一個作業,用JavaScript列印正倒直線,突然覺得自己還是邏輯有待加強訓練啊 document.write("<h3>列印倒正金字塔直線</h3>");//列印一個h3標簽,內容是裡邊的文字 var i= ...
  • 前面的話 數學表達式calc()是CSS中的函數,主要用於數學運算。使用calc()為頁面元素佈局提供了便利和新的思路。本文將介紹calc()的相關內容 定義 數學表達式calc()是calculate計算的縮寫,它允許使用+、-、*、/這四種運算符,可以混合使用%、px、em、rem等單位進行計算 ...
  • 1.viewport 能優化移動瀏覽器的顯示。如果不是響應式網站,不要使用initial-scale或者禁用縮放。大部分4.7-5寸設備的viewport寬設為360px;5.5寸設備設為400px;iphone6設為375px;ipone6 plus設為414px。 <meta name=”vie ...
  • ...
  • 基本語法: 區分大小寫: ECMAScript 中的一切(變數,函數名和操作符)都區分大小寫。 標識符: 表示符就是指,變數,函數,屬性名字,或者函數的參數。 1.第一個字元必須是一個字母,下劃線(_)或美元符號($). 2.其他字元可以是字母,下劃線(_),美元符或數字。 註意:格式為,匈牙利命名 ...
  • Jquery中重置表單的錯誤姿勢 $('#yigeform').reset() 正確姿勢 $('#yigeform')[0].reset() ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...