您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何在C#項目中實現一個Server-sent Events功能,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
基于http協議交互的推送方法大概方法如下:
輪詢(ajax),比較耗費服務器資源。COMET方式(COMET 技術并不是 HTML 5 )
websocket 雙向數據推送,靈活,功能強大
Server-sent-event(簡稱SSE),單項數據推送(Server-sent Events 規范是 HTML 5 規范的一個組成部分)
這里我們研究一下SSE;
Server-sent Events 規范是 HTML 5 規范的一個組成部分,具體的規范文檔見參考資源。該規范比較簡單,主要由兩個部分組成:第一個部分是服務器端與瀏覽器端之間的通訊協議,第二部分則是在瀏覽器端可供 JavaScript 使用的 EventSource 對象。通訊協議是基于純文本的簡單協議。服務器端的響應的內容類型是“text/event-stream”。響應文本的內容可以看成是一個事件流,由不同的事件所組成。每個事件由類型和數據兩部分組成,同時每個事件可以有一個可選的標識符。不同事件的內容之間通過僅包含回車符和換行符的空行(“\r\n”)來分隔。每個事件的數據可能由多行組成。嚴格地說,HTTP協議無法做到服務器主動推送信息。但是有一種變通的發光法,就是服務器向客戶端聲明,接下來要發送的是流信息,也就是說,發送的不是一次性的數據包,而是一個數據流,會連續不斷的發送過來。這是客戶端不會關閉連接,會一直等待服務器發過來的數據流,視頻播放就是這樣的例子。本質上這種通信就是以流信息的方式,完成一次用時很長的下載。
了解了什么是SSE之后就發現這種模式針對后端開發來說是一個巨大的改進,可以像ajax一樣,卻比ajax節省資源;能實現websocket的服務器推送卻不需要更換協議和端口,就像寫一個特別點的api接口一樣方便。跟蹤一下sse的報文顯示
: 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
接下就按如下來分析報文內容:
類型為空白,表示該行是注釋,會在處理時被忽略。
類型為 data,表示該行包含的是數據。以 data 開頭的行可以出現多次。所有這些行都是該事件的數據。
類型為 event,表示該行用來聲明事件的類型。瀏覽器在收到數據時,會產生對應類型的事件。
類型為 id,表示該行用來聲明事件的標識符。
類型為 retry,表示該行用來聲明瀏覽器在連接斷開之后進行再次連接之前的等待時間。
SSE的內容還是很簡潔的,了解了差不多了,現在開始做起來。
1.根據SSE規范對html的頭部進行處理,主要就是添加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數據格式,SSE的數據都是采用UTF8進行處理的
ServerSent(Encoding.UTF8.GetBytes($"id: {id?.Trim()}\nevent: {@event?.Trim()}\ndata: {SerializeHelper.Serialize(t)}\n\n"));
僅需二步就已經完成了SSE服務端的處理了,下面是SAEA.MVC下面的一個完整封裝類EventStream
/**************************************************************************** *項目名稱:SAEA.MVC *CLR 版本:4.0.30319.42000 *機器名稱:WALLE-PC *命名空間:SAEA.MVC *類 名 稱:EventStream *版 本 號:V1.0.0.0 *創建人: yswenli *電子郵箱:yswenli@outlook.com *創建時間:2021/1/6 14:02:09 *描述: *===================================================================== *修改時間:2021/1/6 14:02:09 *修 改 人: yswenli *版 本 號: 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服務器事件流 /// </summary> public class EventStream : ActionResult, IEventStream { /// <summary> /// 最后一次接收到的事件的標識符 /// </summary> public int LastEventID { get; private set; } /// <summary> /// SSE服務器事件流 /// </summary> /// <param name="retry">指定瀏覽器重新發起連接的時間間隔</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); //斷開重連時長 ServerSent(Encoding.UTF8.GetBytes($"retry: {retry}\n\n")); } /// <summary> /// 發送通知 /// </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> /// 發送通知 /// </summary> /// <param name="content"></param> public void ServerSent(byte[] content) { HttpContext.Current.Response.SendData(content); } } }
3.使用EventStream類快速實現服務器推送
將EventStream集成到Controller中,那么在業務繼承類中就可以直接使用封裝好的SSE功能了,如下例:
/**************************************************************************** *項目名稱:SAEA.MVCTest.Controllers *CLR 版本:4.0.30319.42000 *機器名稱:WALLE-PC *命名空間:SAEA.MVCTest.Controllers *類 名 稱:EventStreamController *版 本 號:V1.0.0.0 *創建人: yswenli *電子郵箱:yswenli@outlook.com *創建時間:2021/1/6 13:57:09 *描述: *===================================================================== *修改時間:2021/1/6 13:57:09 *修 改 人: yswenli *版 本 號: 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> /// 發送通知 /// </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(); } } }
了解了SSE技術相關理論,并按理論封裝了EventStream,最后使用EventStream實現了一個推送測試邏輯,接下來就是使用js的EventSource對象在瀏覽器中來驗證了。
創建一個網頁,在html中的js中輸入:
var source = new EventSource("/api/eventstream/sendnotice"); source.onmessage = function (event) { document.getElementById("eventstream").innerHTML += event.data + "<br/>"; };
打開瀏覽器的工發者工具,在網絡選項中查看詳細內容:
關于如何在C#項目中實現一個Server-sent Events功能就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。