C#實(shí)現(xiàn) Server-sent Events的步驟
基于http協(xié)議交互的推送方法大概方法如下:
- 輪詢(ajax),比較耗費(fèi)服務(wù)器資源。COMET方式(COMET 技術(shù)并不是 HTML 5 )
- websocket 雙向數(shù)據(jù)推送,靈活,功能強(qiáng)大
- Server-sent-event(簡(jiǎn)稱SSE),單項(xiàng)數(shù)據(jù)推送(Server-sent Events 規(guī)范是 HTML 5 規(guī)范的一個(gè)組成部分)
這里我們研究一下SSE;
一、什么是SSE
Server-sent Events 規(guī)范是 HTML 5 規(guī)范的一個(gè)組成部分,具體的規(guī)范文檔見參考資源。該規(guī)范比較簡(jiǎn)單,主要由兩個(gè)部分組成:第一個(gè)部分是服務(wù)器端與瀏覽器端之間的通訊協(xié)議,第二部分則是在瀏覽器端可供 JavaScript 使用的 EventSource 對(duì)象。通訊協(xié)議是基于純文本的簡(jiǎn)單協(xié)議。服務(wù)器端的響應(yīng)的內(nèi)容類型是“text/event-stream”。響應(yīng)文本的內(nèi)容可以看成是一個(gè)事件流,由不同的事件所組成。每個(gè)事件由類型和數(shù)據(jù)兩部分組成,同時(shí)每個(gè)事件可以有一個(gè)可選的標(biāo)識(shí)符。不同事件的內(nèi)容之間通過僅包含回車符和換行符的空行(“\r\n”)來分隔。每個(gè)事件的數(shù)據(jù)可能由多行組成。嚴(yán)格地說,HTTP協(xié)議無法做到服務(wù)器主動(dòng)推送信息。但是有一種變通的發(fā)光法,就是服務(wù)器向客戶端聲明,接下來要發(fā)送的是流信息,也就是說,發(fā)送的不是一次性的數(shù)據(jù)包,而是一個(gè)數(shù)據(jù)流,會(huì)連續(xù)不斷的發(fā)送過來。這是客戶端不會(huì)關(guān)閉連接,會(huì)一直等待服務(wù)器發(fā)過來的數(shù)據(jù)流,視頻播放就是這樣的例子。本質(zhì)上這種通信就是以流信息的方式,完成一次用時(shí)很長(zhǎng)的下載。
二、SSE傳輸協(xié)議分析
了解了什么是SSE之后就發(fā)現(xiàn)這種模式針對(duì)后端開發(fā)來說是一個(gè)巨大的改進(jìn),可以像ajax一樣,卻比ajax節(jié)省資源;能實(shí)現(xiàn)websocket的服務(wù)器推送卻不需要更換協(xié)議和端口,就像寫一個(gè)特別點(diǎn)的api接口一樣方便。跟蹤一下sse的報(bào)文顯示
: this is a comment\n reply: 3000\n event: message\n data: first\n\n data: second\n\n id: 100\n event: myevent\n data: third\n\n id: 101\n : this is a comment\n data: fourth\n data: fourth continue\n\n
接下就按如下來分析報(bào)文內(nèi)容:
類型為空白,表示該行是注釋,會(huì)在處理時(shí)被忽略。
類型為 data,表示該行包含的是數(shù)據(jù)。以 data 開頭的行可以出現(xiàn)多次。所有這些行都是該事件的數(shù)據(jù)。
類型為 event,表示該行用來聲明事件的類型。瀏覽器在收到數(shù)據(jù)時(shí),會(huì)產(chǎn)生對(duì)應(yīng)類型的事件。
類型為 id,表示該行用來聲明事件的標(biāo)識(shí)符。
類型為 retry,表示該行用來聲明瀏覽器在連接斷開之后進(jìn)行再次連接之前的等待時(shí)間。
三、C#實(shí)現(xiàn)SSE服務(wù)端
SSE的內(nèi)容還是很簡(jiǎn)潔的,了解了差不多了,現(xiàn)在開始做起來。
1.根據(jù)SSE規(guī)范對(duì)html的頭部進(jìn)行處理,主要就是添加text/event-stream類型,去掉緩存
HttpContext.Current.Response.ContentType = "text/event-stream; charset=utf-8"; HttpContext.Current.Response.SetHeader(ResponseHeaderType.CacheControl, "no-cache"); HttpContext.Current.Response.SetHeader(ResponseHeaderType.KeepAlive, "timeout=5"); HttpContext.Current.Response.Status = HttpStatusCode.OK; HttpContext.Current.Response.SendHeader(-1);
2.封裝SSE數(shù)據(jù)格式,SSE的數(shù)據(jù)都是采用UTF8進(jìn)行處理的
ServerSent(Encoding.UTF8.GetBytes($"id: {id?.Trim()}\nevent: {@event?.Trim()}\ndata: {SerializeHelper.Serialize(t)}\n\n"));
僅需二步就已經(jīng)完成了SSE服務(wù)端的處理了,下面是SAEA.MVC下面的一個(gè)完整封裝類EventStream
/**************************************************************************** *項(xiàng)目名稱:SAEA.MVC *CLR 版本:4.0.30319.42000 *機(jī)器名稱:WALLE-PC *命名空間:SAEA.MVC *類 名 稱:EventStream *版 本 號(hào):V1.0.0.0 *創(chuàng)建人: yswenli *電子郵箱:yswenli@outlook.com *創(chuàng)建時(shí)間:2021/1/6 14:02:09 *描述: *===================================================================== *修改時(shí)間:2021/1/6 14:02:09 *修 改 人: yswenli *版 本 號(hào): V1.0.0.0 *描 述: *****************************************************************************/ using SAEA.Common; using SAEA.Common.Serialization; using SAEA.Common.Threading; using SAEA.Http.Model; using System.Net; using System.Text; namespace SAEA.MVC { /// <summary> /// SSE服務(wù)器事件流 /// </summary> public class EventStream : ActionResult, IEventStream { /// <summary> /// 最后一次接收到的事件的標(biāo)識(shí)符 /// </summary> public int LastEventID { get; private set; } /// <summary> /// SSE服務(wù)器事件流 /// </summary> /// <param name="retry">指定瀏覽器重新發(fā)起連接的時(shí)間間隔</param> public EventStream(int retry = 3 * 1000) { this.ContentEncoding = Encoding.UTF8; if (HttpContext.Current.Request.Headers.ContainsKey("Last-Event-ID")) { if (int.TryParse(HttpContext.Current.Request.Headers["Last-Event-ID"], out int id)) { LastEventID = id; } } HttpContext.Current.Response.ContentType = "text/event-stream; charset=utf-8"; HttpContext.Current.Response.SetHeader(ResponseHeaderType.CacheControl, "no-cache"); HttpContext.Current.Response.SetHeader(ResponseHeaderType.KeepAlive, "timeout=5"); HttpContext.Current.Response.Status = HttpStatusCode.OK; HttpContext.Current.Response.SendHeader(-1); //心跳 var pong = $"SAEAServer PONG {DateTimeHelper.Now:yyyy:MM:dd HH:mm:ss.fff}"; TaskHelper.LongRunning(() => { ServerSent(Encoding.UTF8.GetBytes($": {SerializeHelper.Serialize(pong)}\n\n")); }, 1000); //斷開重連時(shí)長(zhǎng) ServerSent(Encoding.UTF8.GetBytes($"retry: {retry}\n\n")); } /// <summary> /// 發(fā)送通知 /// </summary> /// <param name="str"></param> /// <param name="event"></param> /// <param name="id"></param> public void ServerSent<T>(T t, string @event = "message", string id = "") where T : class { if (t != null) ServerSent(Encoding.UTF8.GetBytes($"id: {id?.Trim()}\nevent: {@event?.Trim()}\ndata: {SerializeHelper.Serialize(t)}\n\n")); } /// <summary> /// 發(fā)送通知 /// </summary> /// <param name="content"></param> public void ServerSent(byte[] content) { HttpContext.Current.Response.SendData(content); } } }
3.使用EventStream類快速實(shí)現(xiàn)服務(wù)器推送
將EventStream集成到Controller中,那么在業(yè)務(wù)繼承類中就可以直接使用封裝好的SSE功能了,如下例:
/**************************************************************************** *項(xiàng)目名稱:SAEA.MVCTest.Controllers *CLR 版本:4.0.30319.42000 *機(jī)器名稱:WALLE-PC *命名空間:SAEA.MVCTest.Controllers *類 名 稱:EventStreamController *版 本 號(hào):V1.0.0.0 *創(chuàng)建人: yswenli *電子郵箱:yswenli@outlook.com *創(chuàng)建時(shí)間:2021/1/6 13:57:09 *描述: *===================================================================== *修改時(shí)間:2021/1/6 13:57:09 *修 改 人: yswenli *版 本 號(hào): V1.0.0.0 *描 述: *****************************************************************************/ using SAEA.MVC; using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace SAEA.MVCTest.Controllers { /// <summary> /// EventStreamController /// </summary> public class EventStreamController : Controller { /// <summary> /// 發(fā)送通知 /// </summary> /// <returns></returns> public ActionResult SendNotice() { try { var es = GetEventStream(); for (int i = 0; ; i++) { var str = $"SAEA.MVC EventStream Test {i}"; es.ServerSent(str); Thread.Sleep(1000); } } catch (Exception ex) { } return Empty(); } } }
四、驗(yàn)證SSE功能
了解了SSE技術(shù)相關(guān)理論,并按理論封裝了EventStream,最后使用EventStream實(shí)現(xiàn)了一個(gè)推送測(cè)試邏輯,接下來就是使用js的EventSource對(duì)象在瀏覽器中來驗(yàn)證了。
創(chuàng)建一個(gè)網(wǎng)頁,在html中的js中輸入:
var source = new EventSource("/api/eventstream/sendnotice"); source.onmessage = function (event) { document.getElementById("eventstream").innerHTML += event.data + "<br/>"; };
打開瀏覽器的工發(fā)者工具,在網(wǎng)絡(luò)選項(xiàng)中查看詳細(xì)內(nèi)容:
以上就是C#實(shí)現(xiàn) Server-sent Events的步驟的詳細(xì)內(nèi)容,更多關(guān)于C#實(shí)現(xiàn) Server-sent Events的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- C#使用SqlServer作為日志數(shù)據(jù)庫(kù)的設(shè)計(jì)與實(shí)現(xiàn)
- SQL Server存儲(chǔ)過程在C#中調(diào)用的簡(jiǎn)單實(shí)現(xiàn)方法
- C#連接SQL Server數(shù)據(jù)庫(kù)的實(shí)例講解
- C#使用SqlConnection連接到SQL Server的代碼示例
- C# Ado.net實(shí)現(xiàn)讀取SQLServer數(shù)據(jù)庫(kù)存儲(chǔ)過程列表及參數(shù)信息示例
- C#連接SQL Server的實(shí)現(xiàn)方法
- C#實(shí)現(xiàn)連接SQL Server2012數(shù)據(jù)庫(kù)并執(zhí)行SQL語句的方法
- 基于C#動(dòng)手實(shí)現(xiàn)網(wǎng)絡(luò)服務(wù)器Web Server
- C#連接到sql server2008數(shù)據(jù)庫(kù)的實(shí)例代碼
- C#實(shí)現(xiàn)的sqlserver操作類實(shí)例
- C#實(shí)現(xiàn)Excel表數(shù)據(jù)導(dǎo)入Sql Server數(shù)據(jù)庫(kù)中的方法
- C#創(chuàng)建一個(gè)小型Web Server(Socket實(shí)現(xiàn))
相關(guān)文章
淺談Visual C#進(jìn)行圖像處理(讀取、保存以及對(duì)像素的訪問)
本文主要介紹利用C#對(duì)圖像進(jìn)行讀取、保存以及對(duì)像素的訪問等操作,介紹的比較簡(jiǎn)單,希望對(duì)初學(xué)者有所幫助。2016-04-04基于Unity實(shí)現(xiàn)3D版2048游戲的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Unity實(shí)現(xiàn)簡(jiǎn)易的3D版2048游戲,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,需要的可以參考一下2023-02-02C# BeginInvoke實(shí)現(xiàn)異步編程方式
這篇文章主要介紹了C# BeginInvoke實(shí)現(xiàn)異步編程方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01C#實(shí)現(xiàn)獲取枚舉中元素個(gè)數(shù)的方法
這篇文章主要介紹了C#實(shí)現(xiàn)獲取枚舉中元素個(gè)數(shù)的方法,是深入理解C#程序設(shè)計(jì)所需要掌握的基本技巧,需要的朋友可以參考下2014-08-08C#數(shù)據(jù)結(jié)構(gòu)與算法揭秘五 棧和隊(duì)列
這節(jié)我們討論了兩種好玩的數(shù)據(jù)結(jié)構(gòu),棧和隊(duì)列2012-11-11