Для проекта мне нужно реализовать SSE (события, отправленные сервером) в приложении С#. Хотя это может показаться легким, я не знаю, как это решить.
Поскольку я новичок в С# (хотя и не новичок в программировании в целом), я совершил экскурсию в Google и попытался найти какой-то образец кода. Из того, что я видел до сих пор, я мог бы научиться создавать HTTP-сервер с С# или потреблять события, отправленные сервером. Но я ничего не нашел о отправке SSE.
Я пытаюсь разобраться: как я могу продолжать отправлять обновленные данные по входящему запросу? Обычно вы получаете запрос, делаете свое дело и отвечаете. Выполнено, соединение закрыто. Но в этом случае я хочу "прилипать" к потоку ответов и отправлять новые данные, каждый раз, когда запускается событие в моем приложении.
Проблема, для меня, заключается в этом основанном на событиях подходе: это не какой-то опрос и обновление на основе intervall. Скорее, это приложение похоже на "Эй, что-то случилось. Я действительно должен рассказать вам об этом!"
TL; DR: как я могу поддерживать этот поток ответов и отправлять обновления - не на основе циклов или таймеров, но каждый раз, когда срабатывают определенные события?
Кроме того, прежде чем я забуду: я знаю, есть библиотеки, которые делают именно это. Но из того, что я видел до сих пор (и из того, что я понял, исправьте меня, если я ошибаюсь), эти решения зависят от ASP.NET/MVC/вы называете это. И поскольку я просто пишу "простое" приложение С#, я не думаю, что отвечаю этим требованиям.
Это звучит неплохо для SignalR. Примечание. SignalR является частью семейства ASP.NET, однако для этого не требуется инфраструктура ASP.NET(System.Web) или IIS, как указано в комментариях.
Чтобы уточнить, SignalR является частью ASP.NET. Согласно их сайту:
ASP.NET - это веб-платформа с открытым исходным кодом для создания современных веб-приложений и сервисов с.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, оно фактически самообслуживается по умолчанию и работает как обычное консольное приложение.
Что касается облегченного сервера, я бы выбрал собственный веб-интерфейс OWIN (https://docs.microsoft.com/en-us/aspnet/web-api/overview/hosting-aspnet-web-api/use-owin- сам-хост-веб-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 автоматически уведомляется. Затем новое событие записывается в правильном формате, отправленном сервером, в поток и сбрасывается. На практике вам нужно было бы отправлять некоторые псевдопинг-события каждую минуту или около того, так как потоки, которые остаются открытыми в течение длительного времени без передачи данных через них, будут закрыты ОС/средой.
Пример кода клиента для проверки этого будет выглядеть так:
Напишите следующий код в асинхронном методе.
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());
}
}
}
}