首頁  >  文章  >  web前端  >  Html5 服務端推送 Server-Sent Event

Html5 服務端推送 Server-Sent Event

黄舟
黄舟原創
2017-02-16 14:28:072261瀏覽


伺服器推送事件(Server-sent Events)是 HTML 5 規範中的一個組成部分,可以用來從服務端即時推送資料到瀏覽器端。相對於類似的 COMET 和 WebSocket 技術來說,伺服器推送事件的使用更簡單,伺服器端的改變也比較小。對於某些類型的應用程式來說,伺服器推送事件是最佳的選擇。

  • WebSocket
    在介紹 HTML 5 伺服器推送事件之前,首先介紹一些上面提到的幾種伺服器端資料推送技術。第一種是 WebSocket。 WebSocket 規範是 HTML 5 中的重要組成部分,已經被許多主流瀏覽器所支持,也有不少基於 WebSocket 開發的應用程式。如名稱所表示的一樣,WebSocket 使用的是套接字連接,基於 TCP 協定。使用 WebSocket 之後,實際上在伺服器端和瀏覽器之間建立一個套接字連接,可以進行雙向的資料傳輸。 WebSocket 的功能是很強大的,使用起來也靈活,可以適用於不同的場景。不過 WebSocket 技術也比較複雜,包括伺服器端和瀏覽器端的實作都不同於一般的 Web 應用。

  • 輪詢
    除了 WebSocket 之外,其他的實作方式是基於 HTTP 協定來達到即時推送的效果。第一種做法是簡易輪詢,也就是瀏覽器端定時向伺服器端發出請求,來查詢是否有資料更新。這種做法比較簡單,可以在一定程度上解決問題。不過對於輪詢的時間間隔需要仔細考慮。輪詢的間隔過長,會導致使用者無法及時接收到更新的資料;輪詢的間隔過短,會導致查詢請求過多,增加伺服器端的負擔。

Html5 服務端推送 Server-Sent Event
缺點:

1:輪詢是由客戶端發起的,那麼在服務端就不能判別我要推送的內容是否已經過期,因為我很難判斷某個訊息是否已經推送給全部的客戶端,那麼服務端就需要快取大量的資料。如果資料保存在資料庫,那麼還要每次請求都需要查詢資料庫,這對資料庫和系統設計都是一個很大的挑戰。

2:請求的頻率太高,每次的請求包中含有同樣的數據,這對pc來說也許算不得什麼,但是對於行動用戶端來講,這應該不是最佳的方案。尤其是遇到還要做權限判斷的時候,那麼服務端的邏輯和效率也會造成使用者體驗的降低。

  • COMET
    COMET 技術改進了簡易輪詢的缺點,使用的是長輪詢。長輪詢的方式在每次請求時,伺服器端會保持該連線在一段時間內處於開啟狀態,而不是在回應完成之後立即關閉。這樣做的好處是在連線處於開啟狀態的時間段內,伺服器端產生的資料更新可以及時地傳回給瀏覽器。當上一個長連線關閉之後,瀏覽器會立即開啟一個新的長連線來繼續請求。不過 COMET 技術的實作在伺服器端和瀏覽器端都需要第三方函式庫的支援。

現在Web App中,大都有Ajax,是這樣子:

Html5 服務端推送 Server-Sent Event
綜合比較上面提到的 4 種不同的技術,簡易輪詢由於其本身的缺陷,並不推薦使用。 COMET 技術並不是 HTML 5 標準的一部分,從相容標準的角度出發,也不建議使用。 WebSocket 規範和伺服器推送技術都是 HTML 5 標準的組成部分,在主流瀏覽器上都提供了原生的支持,是建議使用的。不過 WebSocket 規範更加複雜一些,適用於需要進行複雜雙向資料通訊的場景。對於簡單的伺服器資料推送的場景,使用伺服器推送事件就足夠了。

基於數據推送是這樣的,當數據來源有新數據,它馬上發送到客戶端,不需要等待客戶端請求。這些新數據可能是最新聞,最新股票行情,來自朋友的聊天訊息,天氣預報等。

Html5 服務端推送 Server-Sent Event

資料拉與推的功能是一樣的,使用者拿到新資料。但數據推送有一些優勢。 你可能聽過Comet, Ajax推送, 反向Ajax, HTTP流,WebSockets與SSE是不同的技術。可能最大的優勢是低延遲。 SSE用於web應用程式刷新數據,不需要使用者做任何動作。

WebSockets是實現服務端更加複雜的技術,但它是真的全雙工socket, 服務端能推送資料到客戶端,客戶端也能推送資料回服務端。 SSE工作於存在HTTP/HTTPS協議,支援代理伺服器與認證技術。 SSE是文字協定你能輕易的調試它。如果你需要發送大部二進位資料從服務端到客戶端,WebSocket是更好的選擇。

好在Html5為我們提供了一種方式: Server-Sent Events包含新的HTML元素EventSource和新的MIME類型 text/event-stream 來完成我的需要。

伺服器發送事件API也就是EventSource介面,在你建立一個新的EventSource物件的同時,你可以指定一個接受事件的URI.例如:

var evtSource = new EventSource("ssedemo.php");
<!DOCTYPE html><html><head>
    <title>sever Sent Event实例1</title></head><body>
    <h2>sever Sent Event实例1</h2>
    <p id="result"></p>
    <script type="text/javascript">
        var result = document.getElementById(&#39;result&#39;);        if (typeof (EventSource) !== &#39;undefined&#39;) {            //创建事件源
            var source = new EventSource(&#39;test.php&#39;);            //监听事件源发送过来的数据
            source.onmessage = function(event){
                result.innerHTML +=event.data +&#39;<br>&#39;;
            }
        }else{
            result.innerHTML += "您的浏览器不支持server sent Event";
        }    </script></body></html>
<?php 
    //指定发送事件流的MIME为text/event-stream
    header(&#39;Content-Type:text/event-stream&#39;);    //不缓存服务端发送的数据
    header(&#39;Cache-Control:no-cache&#39;);    //指定服务器发送的事件名
    echo "event:test\n\n";    // 定义服务器向客户端发送的数据
    echo "data:服务器当前时间为:".date(&#39;Y-m-d H:i:s&#39;)."\n\n";    //向客户端发送数据流
    flush(); ?>
  • 把報頭「Content-Type」設定為「text/ event-stream」

  • 規定不對頁面進行快取

  • 輸出發送日期(總是以「data: 」開頭)

  • 向網頁刷新輸出資料

推送的訊息一個使用了”nn”作為結束標誌,經過測試,如果不以”nn”作為結束標誌,那麼客戶端將不能接收到推送的值。還有需要特別聲明一下:推送的資訊格式必須為”data:內容nn“,否則。 。 。
Html5 服務端推送 Server-Sent Event

事件流格式

事件流僅僅是一個簡單的文字資料流,文字應該使用UTF- 8格式的編碼.每條訊息後面都由一個空行作為分隔符號.以冒號開頭的行為註釋行,會被忽略.

每條訊息是由多個字段組成的,每個字段由字段名,一個冒號,以及字段值組成.

字段

  • event
    事件類型

    指定了該欄位,則在客戶端接收到該條訊息時,會在目前的EventSource物件上觸發事件,事件類型就是該欄位的欄位值,你可以使用addEventListener()方法在目前EventSource物件上監聽任意類型的命名事件,如果該條訊息沒有event字段,則會觸發onmessage屬性上的事件處理函數.
  • data

    訊息的資料欄位.如果該條訊息包含多個data欄位,則客戶端會用換行符號把它們連接成一個字串來作為字段值.
  • id

    事件ID,會成為當前EventSource物件的內部屬性」最後一個事件ID」的屬性值.


一個整數值,指定了重新連接的時間(單位為毫秒),如果該字段值不是整數,則會被忽略. 除了上面規定的字段名,其他所有的字段名都會被忽略. Chrome每隔3秒向客戶端推送一次,而FF是每5秒推送一次。 註: 如果一行文字中不包含冒號,則整行文字會被解析成為欄位名稱,其欄位值為空.名稱事件處理方法open當成功與伺服器建立連線時產生onopen
retry
EventSource 物件提供的標準事件
🎜message 🎜error🎜 🎜當出現錯誤時產生🎜🎜onerror🎜🎜🎜🎜
<!DOCTYPE html><html><head>
    <title>sever Sent Event实例1</title></head><body>
    <h2>sever Sent Event实例1</h2>
    <button onclick="closeCnt()">断开连接</button>
    <p id="result"></p>
    <script type="text/javascript">
        var result = document.getElementById(&#39;result&#39;);        if (typeof (EventSource) !== &#39;undefined&#39;) {            //创建事件源
            var source = new EventSource(&#39;test.php&#39;);            //监听事件源发送过来的数据
            source.onmessage = function(event){
                result.innerHTML +=event.data +&#39;<br>&#39;;
            }

            source.onopen = connectionOpen;
            source.onclose = connectionClose;            function connectionOpen(){
                if (source.readyState == 0) {
                    result.innerHTML +=&#39;未建立连接<br>&#39;;
                }                if (source.readyState == 1) {
                    result.innerHTML +=&#39;连接成功<br>&#39;;
                }
            }            function connectionClose(){
                result.innerHTML += "关闭连接,readyState属性值为:" + source.readyState + &#39;<br>&#39;;
            }            function closeCnt(){
                source.close();
                result.innerHTML += "断开连接,readyState属性值为:" + source.readyState + &#39;<br>&#39;;
            }
        }else{
            result.innerHTML += "您的浏览器不支持server sent Event";
        }    </script></body></html>

Html5 服務端推送 Server-Sent Event

在指定 URL 创建出 EventSource 对象之后,可以通过 onmessage 和 addEventListener 方法来添加事件处理方法。当服务器端有新的事件产生,相应的事件处理方法会被调用。EventSource 对象的 onmessage 属性的作用类似于 addEventListener( ‘ message ’ ),不过 onmessage 属性只支持一个事件处理方法。

<!DOCTYPE html><html><head>
    <title>sever Sent Event实例1</title></head><body>
    <h2>sever Sent Event实例1</h2>
    <p id="result"></p>
    <script type="text/javascript">
        var result = document.getElementById(&#39;result&#39;);        if (typeof (EventSource) !== &#39;undefined&#39;) {            //创建事件源
            var source = new EventSource(&#39;test.php&#39;);            //自定义事件源发送过来的数据,事件名和php中事件名对应
            source.addEventListener(&#39;myevent&#39;, &#39;updateRequests&#39;, false);            // source.onmessage = function() {
            //  result.innerHTML = event.data + &#39;<br>&#39;;
            // }
            function updateRequests(event){
                result.innerHTML = event.data + &#39;<br>&#39;;
            }
        }else{
            result.innerHTML += "您的浏览器不支持server sent Event";
        }    </script></body></html>
<?php 
    //指定发送事件流的MIME为text/event-stream
    header(&#39;Content-Type:text/event-stream&#39;);    //不缓存服务端发送的数据
    header(&#39;Cache-Control:no-cache&#39;);    //指定服务器发送的事件名
    echo "event:myevent\n\n";    // 定义服务器向客户端发送的数据
    echo "data:服务器当前时间为:".date(&#39;Y-m-d H:i:s&#39;)."\n\n";    //向客户端发送数据流
    flush(); ?>

前端是HTML5,后端可以是PHP, JSP, Node.js, Asp.net等应用。

服务器推送事件(Server-sent Events)是 HTML 5 规范中的一个组成部分,可以用来从服务端实时推送数据到浏览器端。相对于与之类似的 COMET 和 WebSocket 技术来说,服务器推送事件的使用更简单,对服务器端的改动也比较小。对于某些类型的应用来说,服务器推送事件是最佳的选择。

  • WebSocket
    在介绍 HTML 5 服务器推送事件之前,首先介绍一些上面提到的几种服务器端数据推送技术。第一种是 WebSocket。WebSocket 规范是 HTML 5 中的一个重要组成部分,已经被很多主流浏览器所支持,也有不少基于 WebSocket 开发的应用。正如名称所表示的一样,WebSocket 使用的是套接字连接,基于 TCP 协议。使用 WebSocket 之后,实际上在服务器端和浏览器之间建立一个套接字连接,可以进行双向的数据传输。WebSocket 的功能是很强大的,使用起来也灵活,可以适用于不同的场景。不过 WebSocket 技术也比较复杂,包括服务器端和浏览器端的实现都不同于一般的 Web 应用。

  • 轮询
    除了 WebSocket 之外,其他的实现方式是基于 HTTP 协议来达到实时推送的效果。第一种做法是简易轮询,即浏览器端定时向服务器端发出请求,来查询是否有数据更新。这种做法比较简单,可以在一定程度上解决问题。不过对于轮询的时间间隔需要进行仔细考虑。轮询的间隔过长,会导致用户不能及时接收到更新的数据;轮询的间隔过短,会导致查询请求过多,增加服务器端的负担。

Html5 服務端推送 Server-Sent Event
 缺点:

1:轮询是由客户端发起的,那么在服务端就不能判别我要推送的内容是否已经过期,因为我很难判断某个信息是否已经推送给全部的客户端,那么服务端就需要缓存大量的数据。如果数据保存在数据库,那么还要每次请求都需要查询数据库,这对数据库和系统设计都是一个很大的挑战。

2:请求的频率太高,每次的请求包中含有同样的数据,这对pc来说也许算不得什么,但是对于移动客户端来讲,这应该不是最佳的方案。尤其是遇到还要做权限判断的时候,那么服务端的逻辑和效率也会造成用户体验的降低。

  • COMET
    COMET 技术改进了简易轮询的缺点,使用的是长轮询。长轮询的方式在每次请求时,服务器端会保持该连接在一段时间内处于打开状态,而不是在响应完成之后就立即关闭。这样做的好处是在连接处于打开状态的时间段内,服务器端产生的数据更新可以被及时地返回给浏览器。当上一个长连接关闭之后,浏览器会立即打开一个新的长连接来继续请求。不过 COMET 技术的实现在服务器端和浏览器端都需要第三方库的支持。

现在Web App中,大都有Ajax,是这样子:

Html5 服務端推送 Server-Sent Event
综合比较上面提到的 4 种不同的技术,简易轮询由于其本身的缺陷,并不推荐使用。COMET 技术并不是 HTML 5 标准的一部分,从兼容标准的角度出发,也不推荐使用。WebSocket 规范和服务器推送技术都是 HTML 5 标准的组成部分,在主流浏览器上都提供了原生的支持,是推荐使用的。不过 WebSocket 规范更加复杂一些,适用于需要进行复杂双向数据通讯的场景。对于简单的服务器数据推送的场景,使用服务器推送事件就足够了。

基于数据推送是这样的,当数据源有新数据,它马上发送到客户端,不需要等待客户端请求。这些新数据可能是最新闻,最新股票行情,来自朋友的聊天信息,天气预报等。

Html5 服務端推送 Server-Sent Event

数据拉与推的功能是一样的,用户拿到新数据。但数据推送有一些优势。 你可能听说过Comet, Ajax推送, 反向Ajax, HTTP流,WebSockets与SSE是不同的技术。可能最大的优势是低延迟。SSE用于web应用程序刷新数据,不需要用户做任何动作。

WebSockets是實現服務端更加複雜的技術,但它是真的全雙工socket, 服務端能推送資料到客戶端,客戶端也能推送資料回服務端。 SSE工作於存在HTTP/HTTPS協議,支援代理伺服器與認證技術。 SSE是文字協定你能輕易的調試它。如果你需要發送大部二進位資料從服務端到客戶端,WebSocket是更好的選擇。

好在Html5為我們提供了一種方式: Server-Sent Events包含新的HTML元素EventSource和新的MIME類型 text/event-stream 來完成我的需要。

伺服器發送事件API也就是EventSource介面,在你建立一個新的EventSource物件的同時,你可以指定一個接受事件的URI.例如:

var evtSource = new EventSource("ssedemo.php");
<!DOCTYPE html><html><head>
    <title>sever Sent Event实例1</title></head><body>
    <h2>sever Sent Event实例1</h2>
    <p id="result"></p>
    <script type="text/javascript">
        var result = document.getElementById(&#39;result&#39;);        if (typeof (EventSource) !== &#39;undefined&#39;) {            //创建事件源
            var source = new EventSource(&#39;test.php&#39;);            //监听事件源发送过来的数据
            source.onmessage = function(event){
                result.innerHTML +=event.data +&#39;<br>&#39;;
            }
        }else{
            result.innerHTML += "您的浏览器不支持server sent Event";
        }    </script></body></html>
<?php 
    //指定发送事件流的MIME为text/event-stream
    header(&#39;Content-Type:text/event-stream&#39;);    //不缓存服务端发送的数据
    header(&#39;Cache-Control:no-cache&#39;);    //指定服务器发送的事件名
    echo "event:test\n\n";    // 定义服务器向客户端发送的数据
    echo "data:服务器当前时间为:".date(&#39;Y-m-d H:i:s&#39;)."\n\n";    //向客户端发送数据流
    flush(); ?>
  • 把報頭「Content-Type」設定為「text/ event-stream」

  • 規定不對頁面進行快取

  • 輸出發送日期(總是以「data: 」開頭)

  • 向網頁刷新輸出資料

推送的訊息一個使用了”nn”作為結束標誌,經過測試,如果不以”nn”作為結束標誌,那麼客戶端將不能接收到推送的值。還有需要特別聲明一下:推送的資訊格式必須為”data:內容nn“,否則。 。 。
Html5 服務端推送 Server-Sent Event

事件流格式

事件流僅僅是一個簡單的文字資料流,文字應該使用UTF- 8格式的編碼.每條訊息後面都由一個空行作為分隔符號.以冒號開頭的行為註釋行,會被忽略.

每條訊息是由多個字段組成的,每個字段由字段名,一個冒號,以及字段值組成.

字段

  • event
    事件類型

    指定了該欄位,則在客戶端接收到該條訊息時,會在目前的EventSource物件上觸發事件,事件類型就是該欄位的欄位值,你可以使用addEventListener()方法在目前EventSource物件上監聽任意類型的命名事件,如果該條訊息沒有event字段,則會觸發onmessage屬性上的事件處理函數.
  • data

    訊息的資料欄位.如果該條訊息包含多個data欄位,則客戶端會用換行符號把它們連接成一個字串來作為字段值.
  • id

    事件ID,會成為當前EventSource物件的內部屬性」最後一個事件ID」的屬性值.


一個整數值,指定了重新連接的時間(單位為毫秒),如果該字段值不是整數,則會被忽略. 除了上面規定的字段名,其他所有的字段名都會被忽略. Chrome每隔3秒向客戶端推送一次,而FF是每5秒推送一次。 註: 如果一行文字中不包含冒號,則整行文字會被解析成為欄位名稱,其欄位值為空.名稱事件處理方法open當成功與伺服器建立連線時產生onopen
retry
EventSource 物件提供的標準事件
🎜message 🎜error🎜 🎜當出現錯誤時產生🎜🎜onerror🎜🎜🎜🎜
<!DOCTYPE html><html><head>
    <title>sever Sent Event实例1</title></head><body>
    <h2>sever Sent Event实例1</h2>
    <button onclick="closeCnt()">断开连接</button>
    <p id="result"></p>
    <script type="text/javascript">
        var result = document.getElementById(&#39;result&#39;);        if (typeof (EventSource) !== &#39;undefined&#39;) {            //创建事件源
            var source = new EventSource(&#39;test.php&#39;);            //监听事件源发送过来的数据
            source.onmessage = function(event){
                result.innerHTML +=event.data +&#39;<br>&#39;;
            }

            source.onopen = connectionOpen;
            source.onclose = connectionClose;            function connectionOpen(){
                if (source.readyState == 0) {
                    result.innerHTML +=&#39;未建立连接<br>&#39;;
                }                if (source.readyState == 1) {
                    result.innerHTML +=&#39;连接成功<br>&#39;;
                }
            }            function connectionClose(){
                result.innerHTML += "关闭连接,readyState属性值为:" + source.readyState + &#39;<br>&#39;;
            }            function closeCnt(){
                source.close();
                result.innerHTML += "断开连接,readyState属性值为:" + source.readyState + &#39;<br>&#39;;
            }
        }else{
            result.innerHTML += "您的浏览器不支持server sent Event";
        }    </script></body></html>

Html5 服務端推送 Server-Sent Event

在指定 URL 创建出 EventSource 对象之后,可以通过 onmessage 和 addEventListener 方法来添加事件处理方法。当服务器端有新的事件产生,相应的事件处理方法会被调用。EventSource 对象的 onmessage 属性的作用类似于 addEventListener( ‘ message ’ ),不过 onmessage 属性只支持一个事件处理方法。

<!DOCTYPE html><html><head>
    <title>sever Sent Event实例1</title></head><body>
    <h2>sever Sent Event实例1</h2>
    <p id="result"></p>
    <script type="text/javascript">
        var result = document.getElementById(&#39;result&#39;);        if (typeof (EventSource) !== &#39;undefined&#39;) {            //创建事件源
            var source = new EventSource(&#39;test.php&#39;);            //自定义事件源发送过来的数据,事件名和php中事件名对应
            source.addEventListener(&#39;myevent&#39;, &#39;updateRequests&#39;, false);            // source.onmessage = function() {
            //  result.innerHTML = event.data + &#39;<br>&#39;;
            // }
            function updateRequests(event){
                result.innerHTML = event.data + &#39;<br>&#39;;
            }
        }else{
            result.innerHTML += "您的浏览器不支持server sent Event";
        }    </script></body></html>
<?php 
    //指定发送事件流的MIME为text/event-stream
    header(&#39;Content-Type:text/event-stream&#39;);    //不缓存服务端发送的数据
    header(&#39;Cache-Control:no-cache&#39;);    //指定服务器发送的事件名
    echo "event:myevent\n\n";    // 定义服务器向客户端发送的数据
    echo "data:服务器当前时间为:".date(&#39;Y-m-d H:i:s&#39;)."\n\n";    //向客户端发送数据流
    flush(); ?>

前端是HTML5,后端可以是PHP, JSP, Node.js, Asp.net等应用。

 以上就是Html5 服务端推送 Server-Sent Event的内容,更多相关内容请关注PHP中文网(www.php.cn)!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn