基于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. 项目使用RQ队列的思考

    碎遮项目的后端异步处理经历了 无处理->多线程/多进程->celery异步队列->RQ队列 的调整和修改,先简单说明一下为什么会存在这样的过程. 在nmap的使用指南中,提到过这样的 ...

  2. 题解-洛谷P4859 已经没有什么好害怕的了

    洛谷P4859 已经没有什么好害怕的了 给定 \(n\) 和 \(k\),\(n\) 个糖果能量 \(a_i\) 和 \(n\) 个药片能量 \(b_i\),每个 \(a_i\) 和 \(b_i\) ...

  3. STL—— 容器(vector)数据插入insert()方法 的返回值

    vector 容器下的 insert() 方法拥有返回值,由于insert() 方法拥有4种重载函数,他的返回值不尽相同. 第一种,插入单个元素后的返回值: 1 #include <iostre ...

  4. undo表空间丢失、损坏

    1.模拟误删undo表空间 rm -rf undotbs01.dbf 2.解决步骤 SQL> shutdown immediateORA-01116: error in opening data ...

  5. SpringBoot + Layui +Mybatis-plus实现简单后台管理系统(内置安全过滤器)

    1. 简介   layui(谐音:类UI)是一款采用自身模块规范编写的前端UI框架,遵循原生HTML/CSS/JS的书写与组织形式,门槛极低,拿来即用.其外在极简,却又不失饱满的内在,体积轻盈,组件丰 ...

  6. Unity 3D里相机的平滑跟随(转)

    1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 public class ...

  7. vue第十五单元(熟练使用vue-router插件)

    第十五单元(熟练使用vue-router插件) #课程目标 1.掌握路由嵌套 2.掌握导航守卫 #知识点 #一.路由嵌套 很多时候,我们会在一个视口中实现局部页面的切换.这时候就需要到了嵌套路由. 也 ...

  8. Offer经验分享 - 蚂蚁金服、字节跳动、PDD、百度、华为、Paypal - Java社招面经

    年中的时候因为换工作的缘故,陆续参加了华为.蚂蚁.字节跳动.PDD.百度.Paypal的社招面试,除了字节跳动流程较长,我主动结束面试以外,其他的都顺利拿到了Offer. 最近时间稍微宽裕点了,写个面 ...

  9. SSRF CTF 例题

    一道ctf题目,有两个文件:ssrf3.php和flag.php 题目意思是flag只能127.0.0.1访问,还进行了post验证,这就需要gopher提交post数据来绕过 curl设置了302跳 ...

  10. OpenSNS后台文件上传漏铜分析

    前言 这几天正在想找个文件上传漏洞分析一波,以加深对文件上传漏洞的理解,正好看到FreeBuf的一片文章记对OpenSNS的一次代码审计,由于其只对漏洞进行复现,故在此进行代码层面的分析. 漏洞分析 ...