服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#实现 Server-sent Events的步骤

C#实现 Server-sent Events的步骤

2022-10-26 13:34yswenli C#

这篇文章主要介绍了C#实现 Server-sent Events的步骤,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下

基于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协议无法做到服务器主动推送信息。但是有一种变通的发光法,就是服务器向客户端声明,接下来要发送的是流信息,也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断的发送过来。这是客户端不会关闭连接,会一直等待服务器发过来的数据流,视频播放就是这样的例子。本质上这种通信就是以流信息的方式,完成一次用时很长的下载。

C#实现 Server-sent Events的步骤

二、SSE传输协议分析

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

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

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

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

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

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

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

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

三、C#实现SSE服务端

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

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

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

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

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

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

四、验证SSE功能

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

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

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

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

C#实现 Server-sent Events的步骤

以上就是C#实现 Server-sent Events的步骤的详细内容,更多关于C#实现 Server-sent Events的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/yswenli/p/14246521.html

延伸 · 阅读

精彩推荐
  • C#C# 向Word中设置/更改文本方向的方法(两种)

    C# 向Word中设置/更改文本方向的方法(两种)

    在一般情况下word中输入的文字都是横向的,今天小编给大家带来两种方法来设置更改文本方向的方法,非常不错,对c# word 更改文本方向的知识感兴趣的朋...

    Yesi8522021-12-06
  • C#WPF自定义选择年月控件详解

    WPF自定义选择年月控件详解

    这篇文章主要为大家详细介绍了WPF自定义选择年月控件的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    秋荷雨翔5922022-01-24
  • C#Unity3D实现批量下载图片功能

    Unity3D实现批量下载图片功能

    这篇文章主要为大家详细介绍了Unity3D实现批量下载图片功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    AWNUXCVBN10822022-02-27
  • C#C#中增强类功能的几种方式详解

    C#中增强类功能的几种方式详解

    这篇文章主要给大家介绍了关于C#中增强类功能的几种方式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价...

    Alvin.Lee7982022-03-07
  • C#C# winform程序实现开机自启动并且识别是开机启动还是双击启动

    C# winform程序实现开机自启动并且识别是开机启动还是双击启动

    这篇文章主要介绍了C# winform程序实现开机自启动并且识别是开机启动还是双击启动的实现代码,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要...

    碧水青荷6252022-03-03
  • C#C#程序员统计自己的代码行数

    C#程序员统计自己的代码行数

    这篇文章给大家讲解了下作为程序员如何统计自己写过的代码的行数,这个也是证明自己程序员能力的一个表现,一起来看下。...

    昆明--菜鸟入门9262022-02-15
  • C#c# 如何实现不同进程之间的通信

    c# 如何实现不同进程之间的通信

    这篇文章主要介绍了c# 如何实现不同进程之间的通信,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下...

    一只独行的猿9062022-10-13
  • C#C# partial关键字说明

    C# partial关键字说明

    C# 中可以将类、结构或接口的定义拆分到两个或多个源文件中,在类声明前添加partial关键字即可,通过本文给大家介绍C# partial关键字说明,需要的朋友参...

    java简单例子10672021-11-14