C#实现 Server-sent Events
基于http协议交互的推送方法大概方法如下:
- 轮询(ajax),比较耗费服务器资源。COMET方式(COMET 技术并不是 HTML 5 )
- websocket 双向数据推送,灵活,功能强大
- 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的更多相关文章
- 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 ...
- server sent events
server sent events server push https://html5doctor.com/server-sent-events/ https://developer.mozilla ...
- SQL Server Extended Events 进阶 3:使用Extended Events UI
开始采用Extended Events 最大的阻碍之一是需要使用Xquery和XML知识用来分析数据.创建和运行会话可以用T-SQL完成,但是无论使用什么目标,数据都会被转换为XML.这个限制在SQL ...
- SQL Server Extended Events 进阶 2:使用UI创建基本的事件会话
第一阶中我们描述了如何在Profiler中自定义一个Trace,并且让它运行在服务器端来创建一个Trace文件.然后我们通过Jonathan Kehayias的 sp_SQLskills_Conver ...
- SQL Server Extended Events 进阶 1:从SQL Trace 到Extended Events
http://www.sqlservercentral.com/articles/Stairway+Series/134869/ SQL server 2008 中引入了Extended Events ...
- SQL Server 扩展事件(Extented Events)从入门到进阶(1)——从SQL Trace到Extented Events
由于工作需要,决定深入研究SQL Server的扩展事件(Extended Events/xEvents),经过资料搜索,发现国外大牛的系列文章,作为“学习”阶段,我先翻译这系列文章,后续在工作中的心 ...
- 第十一篇 SQL Server安全审核
本篇文章是SQL Server安全系列的第十一篇,详细内容请参考原文. SQL Server审核SQL Server审核是指你可以在数据库或服务器实例监控事件.审核日志包含你选择捕获的事件的列表,在服 ...
- SQL Server 2008性能故障排查(一)——概论
原文:SQL Server 2008性能故障排查(一)--概论 备注:本人花了大量下班时间翻译,绝无抄袭,允许转载,但请注明出处.由于篇幅长,无法一篇博文全部说完,同时也没那么快全部翻译完,所以按章节 ...
- SQL Server 扩展事件
SQL Server 扩展事件(Extended Event)是用于服务器的常规事件处理系统,是追踪SQL Server系统运行状态的神器,同时也是一个日志记录工具,扩展事件完全可以取代SQL追踪(S ...
- 【译】第十一篇 SQL Server安全审核
本篇文章是SQL Server安全系列的第十一篇,详细内容请参考原文. SQL Server审核SQL Server审核是指你可以在数据库或服务器实例监控事件.审核日志包含你选择捕获的事件的列表,在服 ...
随机推荐
- 第8.30节 重写Python __setattr__方法实现属性修改捕获
一. 引言 在<第8.26节 重写Python类中的__getattribute__方法实现实例属性访问捕获>章节介绍了__getattribute__方法,可以通过重写该方法,截获所有通 ...
- Python学习随笔:使用xlwings读取和操作Execl文件的数字需要注意的问题
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 在使用xlwings读取Excel文件中的数据时,所有的数字不论是整数.浮点数还是文本存放的数字,在 ...
- PyQt(Python+Qt)学习随笔:toolButton的toolButtonStyle属性
toolButtonStyle属性用于确认toolButton按钮显示文字.图标的方式,其类型为枚举类型 Qt.ToolButtonStyle,有如下值: ToolButtonIconOnly(值为0 ...
- kickstart 谷歌 D 2020 年 7 月 12 日 13: 00 - 16: 00
https://codingcompetitions.withgoogle.com/kickstart/round/000000000019ff08/0000000000386d5c (kick st ...
- Hangfire&Autofac与ASP.NET CORE注入失败
Hangfire.Autofac与ASP.NET CORE注入失败 项目里面使用了Hangfire,因为之前没用过吧,遇到了个问题,就是使用了ico容器后,再用Hangfire总是注入不上对象,总是后 ...
- redis学习之——五大基本数据类型
redis 键 (key) 基本数据类型:string 字符串 list (列表) set(集合) hash(类似java 中的Map) zset(有序集合) 官方命令doc redis 键 ...
- window启动mongoDB
windows启动mongo服务 建议使用docker,方便又快捷,可以查看我的其他文章有介绍 创建好日志文件夹后执行以下命令 mongod.exe --logpath "C:\mongod ...
- PluginOK中间件高级版-支持在Chrome、Edge、Firefox等浏览器网页中真正内嵌ActiveX等控件运行的版本已获多家上市公司采购
PluginOK(牛插)中间件(原名:本网通WebRunLocal)是一个实现WEB浏览器(Web Browser)与本地程序(Local Application)之间进行双向调用的低成本.强兼容.安 ...
- Redis数据持久化(RDB、AOF)
1. 简介 Redis作为内存型数据库,数据都保存在内存中,如果重启或意外宕机后,数据会全部丢失.因此,Redis提供了完善的持久化机制,将内存中的数据持久化到磁盘上,避免了完整性和安全性的问题, ...
- mysql 查询 添加 删除 语句
1.说明:创建数据库CREATE DATABASE database-name2.说明:删除数据库drop database dbname3.说明:备份sql server--- 创建 备份数据的 d ...