【问题标题】:Implement sending Server Sent Events in C# (no ASP.NET / MVC / ...)在 C# 中实现发送服务器发送事件(没有 ASP.NET / MVC / ...)
【发布时间】:2017-06-30 17:25:06
【问题描述】:

对于一个项目,我需要在 C# 应用程序中实现 SSE (Server Sent Events)。虽然这听起来很容易,但我不知道如何解决这个问题。

由于我是 C# 新手(尽管对一般编程并不陌生),所以我去 Google 游览并试图寻找一些示例代码。根据我目前所见,我可以学习使用 C# 或 consume 服务器发送事件构建 HTTP 服务器。但是我没有发现任何关于发送 SSE 的信息。

我想弄清楚的是:我怎样才能通过传入的请求继续发送更新的数据? 通常,你收到一个请求,做你的事并回复。完成,连接关闭。但在这种情况下,每次我的应用程序中的事件触发时,我都想“坚持”到响应流并发送新数据。

对我来说,问题在于这种基于事件的方法:它不是基于间隔的轮询和更新。更确切地说,该应用程序就像“嘿,发生了一些事情。我真的应该告诉你!”

TL;DR:我怎样才能保持响应流并发送更新 - 不是基于循环或计时器,而是每次触发某些事件?

另外,在我忘记之前:我知道,那里有图书馆正在这样做。但从我目前所见(以及我所理解的;如果我错了,请纠正我)这些解决方案取决于 ASP.NET / MVC / 你的名字。由于我只是在编写一个“普通”的 C# 应用程序,我认为我不符合这些要求。

【问题讨论】:

  • 不,有些库不需要 ASP.NET。只是为他们研究。即使名称中包含 ASP.NET,也可能只是因为它们由 ASP.NET 团队运行并且可能是自托管的。我也不会将自己局限于 SSE,不要忘记 web sockets 之类的技术。
  • 我不怀疑。我只是说,从到目前为止我所看到的我找不到任何东西。对我来说,迄今为止的结果一直是关于 SSE clients 或 ASP.NET,如果不是的话。正如您可能想象的那样,搜索“服务器端事件服务器库”是毫无用处的。但如果你能给我一个图书馆的链接,我很乐意接受。 :)
  • 出于好奇,您为什么不直接构建一个 ASP.Net 应用程序呢?它只是添加了一些额外的 DLL 依赖项......似乎是一个任意限制。
  • @TimCopenhaver 然后将自己绑定到 IIS。
  • @mason 不正确。您可以在没有 IIS 的情况下使用 ASP.Net - 它们是完全独立的东西,尤其是使用 .Net Core。

标签: c# server-sent-events


【解决方案1】:

至于轻量级服务器,我会使用 OWIN 自托管 WebAPI (https://docs.microsoft.com/en-us/aspnet/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-api)。

一个简单的服务器发送事件服务器操作基本上是这样的:

public class EventController : ApiController
  {
    public HttpResponseMessage GetEvents(CancellationToken clientDisconnectToken)
    {
      var response = Request.CreateResponse();
      response.Content = new PushStreamContent(async (stream, httpContent, transportContext) =>
      {
        using (var writer = new StreamWriter(stream))
        {
          using (var consumer = new BlockingCollection<string>())
          {
            var eventGeneratorTask = EventGeneratorAsync(consumer, clientDisconnectToken);
            foreach (var @event in consumer.GetConsumingEnumerable(clientDisconnectToken))
            {
              await writer.WriteLineAsync("data: " + @event);
              await writer.WriteLineAsync();
              await writer.FlushAsync();
            }
            await eventGeneratorTask;
          }
        }
      }, "text/event-stream");
      return response;
    }

    private async Task EventGeneratorAsync(BlockingCollection<string> producer, CancellationToken cancellationToken)
    {
      try
      {
        while (!cancellationToken.IsCancellationRequested)
        {
          producer.Add(DateTime.Now.ToString(), cancellationToken);
          await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
        }
      }
      finally
      {
        producer.CompleteAdding();
      }
    }
  }

这里的重要部分是 PushStreamContent,它基本上只是发送 HTTP 标头,然后在可用时保持连接打开以写入数据。

在我的示例中,事件是在一个额外任务中生成的,该任务被赋予一个生产者-消费者集合,如果事件可用于集合,则添加事件(这里是每秒的当前时间)。每当有新事件到达时,都会自动通知 GetConsumingEnumerable。然后以正确的服务器发送事件格式将新事件写入流并刷新。在实践中,您需要每分钟左右发送一些伪 ping 事件,因为长时间保持打开而没有通过它们发送数据的流将被操作系统/框架关闭。

用于测试的示例客户端代码如下:

在异步方法中编写以下代码。

using (var client = new HttpClient())
{
  using (var stream = await client.GetStreamAsync("http://localhost:9000/api/event"))
  {
    using (var reader = new StreamReader(stream))
    {
      while (true)
      {
        Console.WriteLine(reader.ReadLine());
      }
    }
  }
}

【讨论】:

  • 为什么要这样做而不是 SignalR?
  • @ckuri 你的看起来是一个完美的答案,我想在桌面 C# WPF 应用程序中使用服务器发送的事件。你能告诉我客户端或监听器的代码应该如何实现吗?谢谢,
  • @datelligence 我已经给出了客户端监听器的网络部分。您当然需要解释 SSE 数据包,而不是 Console.WriteLine,但这很简单。您只需循环调用 reader.ReadLine 即可。空行是数据包结束标记。在此之前,您需要收集一行或多行格式为“key: value”的行,然后在遇到空行时调度/评估。有关详细信息,请参阅w3.org/TR/eventsource/#event-stream-interpretation(忽略仅与浏览器实现相关的 MessageEvent 接口内容)。
  • @mason 我不得不做一些与客户端实现服务器发送事件事件源 (html.spec.whatwg.org/multipage/server-sent-events.html) 类似的事情,而我无法控制客户端实现。否则,SignalR 是流式传输场景的绝佳工具。
【解决方案2】:

这听起来很适合SignalR。注意 SignalR 是 ASP.NET 家族的一部分,但它不需要 ASP.NET 框架 (System.Web) 或 IIS,如 cmets 中所述。

澄清一下,SignalR 是 ASP.NET 的一部分。根据他们的网站:

ASP.NET 是一个用于构建现代 Web 应用程序的开源 Web 框架 和 .NET 服务。 ASP.NET 基于 HTML5、CSS、 和 JavaScript 简单、快速、可扩展至数百万 用户。

SignalR 对 System.Web 或 IIS 没有硬依赖。

您可以自行托管您的 ASP.Net 应用程序(请参阅https://docs.microsoft.com/en-us/aspnet/signalr/overview/deployment/tutorial-signalr-self-host)。如果你使用 .net core,它实际上是默认自托管的,并作为一个普通的控制台应用程序运行。

【讨论】:

  • 我已经偶然发现了这个库,但是 ASP.NET 部分阻止了我。从您链接到的文档来看,我想知道:似乎没有特定于控制台应用程序的部分,对吧?所以我也可以将它包含在我的 VSTO 插件中吗?或者这会导致线程方面的问题(即服务器阻塞进一步的代码路径)?
  • 嗯...从技术上讲,SignalR 是 asp.net 的一部分。说它不需要它似乎有点误导 - 这类似于说 Web 窗体不需要 Asp.Net。也许我们只是对“Asp.net”的定义有点不同?
  • SignalR 可能在 ASP.NET 品牌名称下,但是当有人说“使用 ASP.NET”或“需要 ASP.NET”时,这通常意味着 System.Web 再次与 IIS 相关联。 Web 窗体绝对需要 ASP.NET、System.Web 和 IIS。如果我们谈论的是 Web API,那么它对 ASP.NET、System.Web 或 IIS 没有硬依赖。在对 ASP.NET 的依赖方面,Web API 和 SignalR 在同一条船上。我只是希望你修正你的第一段,以免我们提供不正确或误导性的信息。
  • 我从未说过它依赖于“System.Web”。 ASP.Net Core 实际上根本不依赖 System.Web。我认为您正在阅读不存在的解释。我会更新澄清说明,但我不认为信息有误。
  • 我看到你已经编辑了你的帖子,我也让它更清楚了一点。如果您认为合适,请随意扭转我的观点,但我认为它可以提高理解力。
猜你喜欢
  • 1970-01-01
  • 2022-01-22
  • 2012-02-16
  • 1970-01-01
  • 1970-01-01
  • 2023-02-17
  • 2012-04-22
  • 2020-08-06
  • 2021-11-05
相关资源
最近更新 更多