mh1*_*166 2 c# server-sent-events
对于一个项目,我需要在C#应用程序中实现SSE(服务器发送事件)。尽管这听起来很简单,但是我不知道如何解决这个问题。
由于我是C#的新手(尽管通常不是编程新手),所以我参观了Google,并尝试查找一些示例代码。到目前为止,我可以学习使用C#构建HTTP Server或使用服务器发送的事件。但是我没有发现发送SSE的信息。
我要设法解决的问题:如何继续通过传入请求发送更新的数据?通常,您会收到请求,做您的事情并回复。完成,连接已关闭。但是在这种情况下,每次我的应用程序中的事件触发时,我都希望“粘”在响应流上并通过它发送新的数据。
对我而言,问题在于这种基于事件的方法:它不是基于间隔的轮询和更新。而是该应用程序的运行方式类似于“嘿,发生了什么事。我真的应该告诉你!”
TL; DR:如何保持响应流并发送更新-不是基于循环或计时器,而是每当某些事件触发时?
另外,在我忘记之前:我知道,那里有图书馆就是这样做的。但是从目前为止我所看到的(以及我所了解的;如果我错了,请纠正我),这些解决方案取决于ASP.NET/MVC/您的名字。在编写“普通” C#应用程序时,我认为我不满足这些要求。
对于轻量级服务器,我将使用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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这里的重要部分是PushStreamContent,它基本上只是发送HTTP标头,然后保持连接开放以在可用时写入数据。
在我的示例中,事件是在额外任务中生成的,该任务被赋予生产者-消费者集合,并添加事件(此处为每秒的当前时间)(如果集合可用)。每当有新事件到达时,都会自动通知GetConsumingEnumerable。然后,将新事件以正确的服务器发送事件格式写入流中并进行刷新。在实践中,您将需要每分钟左右发送一些伪ping事件,因为长时间处于打开状态而没有通过它们发送数据的流将被OS /框架关闭。
用于测试此内容的示例客户端代码如下:
用异步方法编写以下代码。
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());
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这听起来很适合SignalR。注意 SignalR 是 ASP.NET 系列的一部分,但这不需要 ASP.NET 框架 (System.Web) 或 IIS,如注释中所述。
澄清一下,SignalR 是 ASP.NET 的一部分。根据他们的网站:
ASP.NET 是一个开源 Web 框架,用于使用 .NET 构建现代 Web 应用程序和服务。ASP.NET 创建基于 HTML5、CSS 和 JavaScript 的网站,这些网站简单、快速并且可以扩展到数百万用户。
SignalR 对 System.Web 或 IIS 没有硬依赖。
您可以自行托管 ASP.Net 应用程序(请参阅https://learn.microsoft.com/en-us/aspnet/signalr/overview/deployment/tutorial-signalr-self-host)。如果您使用.net core,它实际上默认是自托管的,并作为普通控制台应用程序运行。
| 归档时间: |
|
| 查看次数: |
7766 次 |
| 最近记录: |