基于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. 第三十五章、PyQt输入部件:QFontComboBox、QLineEdit、QTextEdit、QPlainText功能详解

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.引言 输入部件量比较多,且功能很丰富,但除了用于编写编辑器.浏览器 ...

  2. 为什么Python中称__lt__、__gt__等为“富比较”方法

    Python中基类object提供了一系列可以用于实现同类对象进行"比较"的方法,可以用于同类对象的不同实例进行比较,包括__lt__.__gt__.__le__.__ge__._ ...

  3. node.js、yarn、npm到底是什么?

    最近在部署环境,在没有开发脚本的情况下,自己根据以往其他项目中的脚本去生搬硬套,发现很难对项目的配置成功.对配置不成功的情况进行判断,发现是对脚本不熟悉,不了解其原理,实现方式也不知道,所以抽时间去了 ...

  4. 半夜删你代码队 Day3冲刺

    一.每日站立式会议 1.站立式会议 成员 昨日完成工作 今日计划工作 遇到的困难 陈惠霖 了解相关网页设计 了解相关网页设计 无 侯晓龙 写了第一个例子 尝试写第一个实例子 无 周楚池 学习 与余金龙 ...

  5. 树莓派开发笔记(十一):蓝牙的使用,BlueZ协议(双树莓探测rssi并通过蓝牙互传获取的rssi信号强度)

    若该文为原创文章,转载请注明原文出处本文章博客地址:https://blog.csdn.net/qq21497936/article/details/110940484长期持续带来更多项目与技术分享, ...

  6. linux 设置别名

    [root@oldboyedu45-xy data]# alias(查找别名) alias cp='cp -i' alias l.='ls -d .* --color=auto' alias ll=' ...

  7. Jmeter(6)命令行执行

    Jmeter执行方式有2种:GUI和非GUI模式 GUI:在Windows电脑上运行,图形化界面,可直接查看测试结果,但是消耗压力机资源较高 非GUI:通过命令行执行,无图形化界面,不方便查看测试结果 ...

  8. python3.6连接数据库 小微工作笔记

    连接141 老虎钱包数据库方法,可以连接成功(MYSQL)1 import pymysql 2 conn = pymysql.connect(host='192.168.1.141', port=33 ...

  9. layui的基本使用

    打开官网https://www.layui.com/下载这个框架 官网首页 下载到 layui 的最新版,它经过了自动化构建,更适合用于生产环境.目录结构如下 ├─css //css目录 │ │─mo ...

  10. 初学者迭代python

    #汉诺塔 def hanni(n,A,B,C): if n == 1: print (A,'-->',C) else: # 将n-1个盘子移动到B上 hanni(n-1,A,C,B) # 将第n ...