Implement sending Server Sent Events in C# (no ASP.NET / MVC / …)

前端 未结 2 1711
小蘑菇
小蘑菇 2021-02-06 12:32

For a project, I need to implement SSE (Server Sent Events) in a C# Application. Although this may sound easy, I\'ve got no clue how to solve this.

As I\'m new to C# (th

2条回答
  •  太阳男子
    2021-02-06 13:30

    As for a light-weight server I would go with an OWIN selfhost WebAPI (https://docs.microsoft.com/en-us/aspnet/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-api).

    A simple server-sent event server action would basically go like:

    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())
              {
                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 producer, CancellationToken cancellationToken)
        {
          try
          {
            while (!cancellationToken.IsCancellationRequested)
            {
              producer.Add(DateTime.Now.ToString(), cancellationToken);
              await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
            }
          }
          finally
          {
            producer.CompleteAdding();
          }
        }
      }
    

    The important part here is the PushStreamContent, which basically just sends the HTTP headers and then leaves the connection open to write the data when it is available.

    In my example the events are generated in an extra-task which is given a producer-consumer collection and adds the events (here the current time every second) if they are available to the collection. Whenever a new event arrives GetConsumingEnumerable is automatically notified. The new event is then written in the proper server-sent event format to the stream and flushed. In practice you would need to send some pseudo-ping events every minute or so, as streams which are left open for a long time without data being sent over them would be closed by the OS/framework.

    The sample client code to test this would go like:

    Write the following code in async method.

    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());
          }
        }
      }
    }
    

提交回复
热议问题