c# 实现 Server-Sent Events (SSE),服务器单方面消息推送 [案例版]

游览器通讯技术其实有很多,相较于 WebSocket 而言,Server-Sent Events (简称SSE)更少被人知晓,具体实践也较少。

但是,实现却是简单的,其中 IE / Edge 几乎根本不支持 SSE。

这也是WebSocket活的原因,相比 websocket的复杂来讲,SSE相对来讲实现就简单多了,如果WebSocket是全双工,那么SSE就是半双工通信。

而且,在.NET 技术栈里 SignalR 其实是包含了前端的ajax通讯、websocket通讯以及SSE(游览器事件)通讯。

但是也不影响,了解一下SSE的魅力。

SSE 协议原理

SSE 协议很简单,本质上是一个客户端发起的 HTTP Get 请求,服务器在接到该请求后,返回 200 OK 状态,同时附带以下 Headers

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

SSE 的 MIME Type 规定为 text/event-stream

SSE 肯定不允许缓存

SSE 是一个一直打开的 TCP 连接,所以 Connection 为 Keep-Alive

SSE协议通讯基本格式

[field]: value\n

其中 [field] 中的field 可以取四种key

  1. id
  2. data
  3. event
  4. retry

id

数据标识符用id字段表示,相当于每一条数据的编号。

浏览器用lastEventId属性读取这个值。一旦连接断线,浏览器会发送一个 HTTP 头,里面包含一个特殊的Last-Event-ID头信息,将这个值发送回来,用来帮助服务器端重建连接。因此,这个头信息可以被视为一种同步机制。

data

数据内容用data字段表示。

如果数据很长,可以分成多行,最后一行用\n\n结尾,前面行都用\n结尾。

event

event字段表示自定义的事件类型,默认是message事件。浏览器可以用addEventListener()监听该事件。

retry

服务器可以用retry字段,指定浏览器重新发起连接的时间间隔。

代码实现

在实际情况中 可能用不上SignalR 或者 websocket 这样全双工,太重的框架和技术,我们就可以用基于http协议本身的sse协议的实现我们想要的推送功能。

ASP.NET MVC

HomeController

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
 
        public ActionResult ServerSentEvents()
        {
            Random random = new Random();
            string _event = "message";
            string data = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
            if (random.Next(0, 10) % 3 == 0)
            {
                data = "新消息";
                _event = "NewMsg";
            }
 
            Response.ContentType = "text/event-stream";
            return Content($"retry:{1000}\nevent:{_event}\nid:{DateTime.Now.Ticks}\ndata:{data}\n\n");
        }
    }

Index.cshtml

<h>主页</h>
<div id="result"></div>
<script type="text/javascript">
    if (typeof (EventSource) !== "undefined") {
        var source = new EventSource("../home/ServerSentEvents");
        source.onopen = function (event) {
            console.log(source.readyState);
            console.log(event);
        };
        source.onerror = function (event) {
            console.log(source.readyState);
            console.log(event);
            document.getElementById("result").innerHTML += "服务关闭 <br>";
        };
        source.onmessage = function (event) {
            console.log(source.readyState);
            console.log(event);
            document.getElementById("result").innerHTML += event.data + "<br>";
        };
        source.addEventListener("NewMsg", function (e) {
            console.log("唤醒新事件");
            console.log(e);
            document.getElementById("result").innerHTML += e.data + "<br>";
        });
    }
    else {
        document.getElementById("result").innerHTML = "抱歉,你的浏览器不支持 server-sent 事件...";
    }
</script>

结果如下

实现结果如下:

以前一直不懂,现在懂了。也实现了。

又掌握一个知识点,欧耶。

原理参考

可以参考这一篇的 概念 https://zhuanlan.zhihu.com/p/21308648

或者 阮一峰 大佬的 这一篇 https://www.ruanyifeng.com/blog/2017/05/server-sent_events.html

代码地址

https://github.com/kesshei/WebSSEDemo.git

https://gitee.com/kesshei/WebSSEDemo

posted @ 2022-06-14 12:07  蓝创精英团队  阅读(10)  评论(0)    收藏  举报  来源