对于项目,我需要在C#应用程序中实现SSE(Server Sent Events)。虽然这听起来很容易,但我不知道如何解决这个问题。
由于我是C#的新手(虽然,对于一般的编程并不陌生)我去了一次游览Google,并试图寻找一些示例代码。从我目前所见,我可以学习使用C#或使用服务器发送事件来构建HTTP服务器。但我发现没有发送SSE。
我想要解决的问题是:如何继续通过传入请求发送更新数据?通常,您会收到请求,做您的事情并回复。完成,连接关闭。但在这种情况下,我希望能够“坚持”#34;每当我的应用程序中的事件触发时,响应流并发送新数据。
对我来说,问题在于这种基于事件的方法:它不是一些基于intervall的轮询和更新。相反,应用程序就像#34;嘿,发生了什么。我真的应该告诉你一些事情!"
TL; DR:如何保持该响应流并发送更新 - 不是基于循环或定时器,而是每次某些事件触发?
另外,在我忘记之前:我知道,那里有图书馆。但是从我到目前为止所看到的情况(以及我所理解的情况;如果我错了,请纠正我),这些解决方案依赖于ASP.NET / MVC /您的名字。因为我只是写一个"普通" C#应用程序,我不认为我符合这些要求。
答案 0 :(得分:4)
对于轻量级服务器,我会使用OWIN selfhost 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事件,因为很长时间没有数据发送的流将被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());
}
}
}
}
答案 1 :(得分:2)
这听起来非常适合SignalR。注意SignalR是ASP.NET系列的一部分,但是这不需要ASP.NET框架(System.Web)或IIS,如评论中所述。
为了澄清,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核心,它实际上是默认自托管的,并作为普通的控制台应用程序运行。