基于http协议交互的推送方法大概方法如下:

  1. 轮询(ajax),比较耗费服务器资源。COMET方式(COMET 技术并不是 HTML 5 )
  2. websocket 双向数据推送,灵活,功能强大
  3. Server-sent-event(简称SSE),单项数据推送(Server-sent Events 规范是 HTML 5 规范的一个组成部分)

这里我们研究一下SSE;

一、什么是SSE

Server-sent Events 规范是 HTML 5 规范的一个组成部分,具体的规范文档见参考资源。该规范比较简单,主要由两个部分组成:第一个部分是服务器端与浏览器端之间的通讯协议,第二部分则是在浏览器端可供 JavaScript 使用的 EventSource 对象。通讯协议是基于纯文本的简单协议。服务器端的响应的内容类型是“text/event-stream”。响应文本的内容可以看成是一个事件流,由不同的事件所组成。每个事件由类型和数据两部分组成,同时每个事件可以有一个可选的标识符。不同事件的内容之间通过仅包含回车符和换行符的空行(“\r\n”)来分隔。每个事件的数据可能由多行组成。严格地说,HTTP协议无法做到服务器主动推送信息。但是有一种变通的发光法,就是服务器向客户端声明,接下来要发送的是流信息,也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断的发送过来。这是客户端不会关闭连接,会一直等待服务器发过来的数据流,视频播放就是这样的例子。本质上这种通信就是以流信息的方式,完成一次用时很长的下载。

二、SSE传输协议分析

了解了什么是SSE之后就发现这种模式针对后端开发来说是一个巨大的改进,可以像ajax一样,却比ajax节省资源;能实现websocket的服务器推送却不需要更换协议和端口,就像写一个特别点的api接口一样方便。跟踪一下sse的报文显示,

 1 : this is a comment\n
2 reply: 3000\n
3 event: message\n
4 data: first\n\n
5 data: second\n\n
6 id: 100\n
7 event: myevent\n
8 data: third\n\n
9 id: 101\n
10 : this is a comment\n
11 data: fourth\n
12 data: fourth continue\n\n

接下就按如下来分析报文内容:

类型为空白,表示该行是注释,会在处理时被忽略。

类型为 data,表示该行包含的是数据。以 data 开头的行可以出现多次。所有这些行都是该事件的数据。

类型为 event,表示该行用来声明事件的类型。浏览器在收到数据时,会产生对应类型的事件。

类型为 id,表示该行用来声明事件的标识符。

类型为 retry,表示该行用来声明浏览器在连接断开之后进行再次连接之前的等待时间。

三、C#实现SSE服务端

SSE的内容还是很简洁的,了解了差不多了,现在开始做起来。

1.根据SSE规范对html的头部进行处理,主要就是添加text/event-stream类型,去掉缓存

1 HttpContext.Current.Response.ContentType = "text/event-stream; charset=utf-8";
2 HttpContext.Current.Response.SetHeader(ResponseHeaderType.CacheControl, "no-cache");
3 HttpContext.Current.Response.SetHeader(ResponseHeaderType.KeepAlive, "timeout=5");
4 HttpContext.Current.Response.Status = HttpStatusCode.OK;
5 HttpContext.Current.Response.SendHeader(-1);

2.封装SSE数据格式,SSE的数据都是采用UTF8进行处理的

1 ServerSent(Encoding.UTF8.GetBytes($"id: {id?.Trim()}\nevent: {@event?.Trim()}\ndata: {SerializeHelper.Serialize(t)}\n\n"));

仅需二步就已经完成了SSE服务端的处理了,下面是SAEA.MVC下面的一个完整封装类EventStream

 1 /****************************************************************************
2 *项目名称:SAEA.MVC
3 *CLR 版本:4.0.30319.42000
4 *机器名称:WALLE-PC
5 *命名空间:SAEA.MVC
6 *类 名 称:EventStream
7 *版 本 号:V1.0.0.0
8 *创建人: yswenli
9 *电子邮箱:yswenli@outlook.com
10 *创建时间:2021/1/6 14:02:09
11 *描述:
12 *=====================================================================
13 *修改时间:2021/1/6 14:02:09
14 *修 改 人: yswenli
15 *版 本 号: V1.0.0.0
16 *描 述:
17 *****************************************************************************/
18 using SAEA.Common;
19 using SAEA.Common.Serialization;
20 using SAEA.Common.Threading;
21 using SAEA.Http.Model;
22 using System.Net;
23 using System.Text;
24
25 namespace SAEA.MVC
26 {
27 /// <summary>
28 /// SSE服务器事件流
29 /// </summary>
30 public class EventStream : ActionResult, IEventStream
31 {
32 /// <summary>
33 /// 最后一次接收到的事件的标识符
34 /// </summary>
35 public int LastEventID
36 {
37 get;
38 private set;
39 }
40
41 /// <summary>
42 /// SSE服务器事件流
43 /// </summary>
44 /// <param name="retry">指定浏览器重新发起连接的时间间隔</param>
45 public EventStream(int retry = 3 * 1000)
46 {
47 this.ContentEncoding = Encoding.UTF8;
48
49 if (HttpContext.Current.Request.Headers.ContainsKey("Last-Event-ID"))
50 {
51 if (int.TryParse(HttpContext.Current.Request.Headers["Last-Event-ID"], out int id))
52 {
53 LastEventID = id;
54 }
55 }
56
57 HttpContext.Current.Response.ContentType = "text/event-stream; charset=utf-8";
58 HttpContext.Current.Response.SetHeader(ResponseHeaderType.CacheControl, "no-cache");
59 HttpContext.Current.Response.SetHeader(ResponseHeaderType.KeepAlive, "timeout=5");
60 HttpContext.Current.Response.Status = HttpStatusCode.OK;
61 HttpContext.Current.Response.SendHeader(-1);
62
63 //心跳
64 var pong = $"SAEAServer PONG {DateTimeHelper.Now:yyyy:MM:dd HH:mm:ss.fff}";
65
66 TaskHelper.LongRunning(() =>
67 {
68 ServerSent(Encoding.UTF8.GetBytes($": {SerializeHelper.Serialize(pong)}\n\n"));
69 }, 1000);
70
71 //断开重连时长
72 ServerSent(Encoding.UTF8.GetBytes($"retry: {retry}\n\n"));
73 }
74 /// <summary>
75 /// 发送通知
76 /// </summary>
77 /// <param name="str"></param>
78 /// <param name="event"></param>
79 /// <param name="id"></param>
80 public void ServerSent<T>(T t, string @event = "message", string id = "") where T : class
81 {
82 if (t != null)
83 ServerSent(Encoding.UTF8.GetBytes($"id: {id?.Trim()}\nevent: {@event?.Trim()}\ndata: {SerializeHelper.Serialize(t)}\n\n"));
84 }
85 /// <summary>
86 /// 发送通知
87 /// </summary>
88 /// <param name="content"></param>
89 public void ServerSent(byte[] content)
90 {
91 HttpContext.Current.Response.SendData(content);
92 }
93 }
94 }

3.使用EventStream类快速实现服务器推送

将EventStream集成到Controller中,那么在业务继承类中就可以直接使用封装好的SSE功能了,如下例:

 1 /****************************************************************************
2 *项目名称:SAEA.MVCTest.Controllers
3 *CLR 版本:4.0.30319.42000
4 *机器名称:WALLE-PC
5 *命名空间:SAEA.MVCTest.Controllers
6 *类 名 称:EventStreamController
7 *版 本 号:V1.0.0.0
8 *创建人: yswenli
9 *电子邮箱:yswenli@outlook.com
10 *创建时间:2021/1/6 13:57:09
11 *描述:
12 *=====================================================================
13 *修改时间:2021/1/6 13:57:09
14 *修 改 人: yswenli
15 *版 本 号: V1.0.0.0
16 *描 述:
17 *****************************************************************************/
18 using SAEA.MVC;
19 using System;
20 using System.Collections.Generic;
21 using System.Text;
22 using System.Threading;
23
24 namespace SAEA.MVCTest.Controllers
25 {
26 /// <summary>
27 /// EventStreamController
28 /// </summary>
29 public class EventStreamController : Controller
30 {
31 /// <summary>
32 /// 发送通知
33 /// </summary>
34 /// <returns></returns>
35 public ActionResult SendNotice()
36 {
37 try
38 {
39 var es = GetEventStream();
40
41 for (int i = 0; ; i++)
42 {
43 var str = $"SAEA.MVC EventStream Test {i}";
44
45 es.ServerSent(str);
46
47 Thread.Sleep(1000);
48 }
49 }
50 catch (Exception ex)
51 {
52
53 }
54 return Empty();
55 }
56 }
57 }

四、验证SSE功能

了解了SSE技术相关理论,并按理论封装了EventStream,最后使用EventStream实现了一个推送测试逻辑,接下来就是使用js的EventSource对象在浏览器中来验证了。

创建一个网页,在html中的js中输入:

1         var source = new EventSource("/api/eventstream/sendnotice");
2 source.onmessage = function (event) {
3 document.getElementById("eventstream").innerHTML += event.data + "<br/>";
4 };

打开浏览器的工发者工具,在网络选项中查看详细内容:

转载请标明本文来源:https://www.cnblogs.com/yswenli/p/14246521.html
更多内容欢迎我的的github:https://github.com/yswenli/SAEA
如果发现本文有什么问题和任何建议,也随时欢迎交流~

 

C#实现 Server-sent Events的更多相关文章

  1. Play Framework, Server Sent Events and Internet Explorer

    http://www.tuicool.com/articles/7jmE7r Next week I will be presenting at Scala Days . In my talk I w ...

  2. server sent events

    server sent events server push https://html5doctor.com/server-sent-events/ https://developer.mozilla ...

  3. SQL Server Extended Events 进阶 3:使用Extended Events UI

    开始采用Extended Events 最大的阻碍之一是需要使用Xquery和XML知识用来分析数据.创建和运行会话可以用T-SQL完成,但是无论使用什么目标,数据都会被转换为XML.这个限制在SQL ...

  4. SQL Server Extended Events 进阶 2:使用UI创建基本的事件会话

    第一阶中我们描述了如何在Profiler中自定义一个Trace,并且让它运行在服务器端来创建一个Trace文件.然后我们通过Jonathan Kehayias的 sp_SQLskills_Conver ...

  5. SQL Server Extended Events 进阶 1:从SQL Trace 到Extended Events

    http://www.sqlservercentral.com/articles/Stairway+Series/134869/ SQL server 2008 中引入了Extended Events ...

  6. SQL Server 扩展事件(Extented Events)从入门到进阶(1)——从SQL Trace到Extented Events

    由于工作需要,决定深入研究SQL Server的扩展事件(Extended Events/xEvents),经过资料搜索,发现国外大牛的系列文章,作为“学习”阶段,我先翻译这系列文章,后续在工作中的心 ...

  7. 第十一篇 SQL Server安全审核

    本篇文章是SQL Server安全系列的第十一篇,详细内容请参考原文. SQL Server审核SQL Server审核是指你可以在数据库或服务器实例监控事件.审核日志包含你选择捕获的事件的列表,在服 ...

  8. SQL Server 2008性能故障排查(一)——概论

    原文:SQL Server 2008性能故障排查(一)--概论 备注:本人花了大量下班时间翻译,绝无抄袭,允许转载,但请注明出处.由于篇幅长,无法一篇博文全部说完,同时也没那么快全部翻译完,所以按章节 ...

  9. SQL Server 扩展事件

    SQL Server 扩展事件(Extended Event)是用于服务器的常规事件处理系统,是追踪SQL Server系统运行状态的神器,同时也是一个日志记录工具,扩展事件完全可以取代SQL追踪(S ...

  10. 【译】第十一篇 SQL Server安全审核

    本篇文章是SQL Server安全系列的第十一篇,详细内容请参考原文. SQL Server审核SQL Server审核是指你可以在数据库或服务器实例监控事件.审核日志包含你选择捕获的事件的列表,在服 ...

随机推荐

  1. 第二十六章、containers容器类部件QToolBox工具箱详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.概述 容器部件就是可以在部件内放置其他部件的部件,在Qt Designer中可以使用的容器部件有 ...

  2. Hbase的基本原理(与HIVE的区别、数据结构模型、拓扑结构、水平分区原理、场景)

    重点:HBase的基本数据模型.拓扑结构.部署配置方法,并介绍通过命令行和编程方式使用HBase的基本方法. HBase:一种列存储模式与键值对相结合的NoSQL软件,但更多的是使用列存储模式,底层的 ...

  3. Java基础学习之数据类型、基础语法与数组(3)

    目录 1.数据类型 1.1.基本数据类型 1.2.引用数据类型 1.3.自动装箱与拆箱 2.基础语法 2.1.标识符 2.2.修饰符 2.2.1.访问控制修饰符 2.2.2.非访问控制修饰符 2.3. ...

  4. 重磅!Panda Global获悉立陶宛下周将发行区块链数字货币!

    近日,Panda Global从路透社获悉,立陶宛将在下周开始预售2.4万枚由央行发行的数字货币.该名为LBCoin的数字货币基于区块链技术生产,也是该国试点具有国家支持背景的数字货币和区块链技术的项 ...

  5. CF392B Tower of Hanoi

    题目链接. Description 三塔汉诺塔问题,给一个 \(3 \times 3\) 的矩阵 \(t\),\(t_{i, j}\) 表示从 \(i\) 塔移动一个盘子到 \(j\) 塔的花费. 初 ...

  6. nginx 静态化合集(去掉index.php目录)

    访问某域名时,去掉index.php目录时达到效果一样 如:www.test1/index.php/test2跟www.test1/test2效果一致 在vhosts.conf中加重写就可以了 loc ...

  7. STL——容器(Set & multiset)编译器提供的16种构造(挖个坑)

    Set & multiset 在vs2019编译器中提供了16种构造方法 1.默认的无参构造 2.比较容器内容,key_comp()函数返回一个比较key的函数. 3.使用迭代器的区间拷贝,拷 ...

  8. Excel-HLOOKUP函数匹配查找②

    问题场景 绩效奖金评定发放,针对表中的考核员工,先按考核总分评级,再根据根据分级评定绩效奖金. 场景一 在考核员工表中,根据员工的考核总分将其分为四个等级(可根据业务场景和实际情况分析):A级分数区间 ...

  9. Javascript 根据文件名判断是否未图片

    var isImage = (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(filename)

  10. python去除文件中重复的行

    去除文件中重复的行 import os with open('db.txt','r',encoding='utf-8') as read_f,\ open('.db.txt.swap','w',enc ...