C# – How to consume an SSE endpoint with HttpClient

Server-Sent Events (SSE) allow a client to subscribe to messages from the server. It creates a one-way stream from the server to the client. When the server has new messages for the client, it writes them to the stream. This is an alternative to the client polling the server for updates.

Use the following to consume an SSE endpoint with HttpClient:

using (var streamReader = new StreamReader(await httpClient.GetStreamAsync(url)))
{
	while (!streamReader.EndOfStream)
	{
		var message = await streamReader.ReadLineAsync();
		Console.WriteLine($"Received message: {message}");
	}
}
Code language: C# (cs)

Take a look at the example console app below to see this used in context.

Example – Simple SSE client console app

When I’m developing an SSE endpoint, I like to use the following simple console app for consuming the endpoint.

static async Task Main(string[] args)
{
	HttpClient client = new HttpClient();
	client.Timeout = TimeSpan.FromSeconds(5);
	string stockSymbol = "VTSAX";
	string url = $"http://localhost:9000/stockpriceupdates/{stockSymbol}";

	while (true)
	{
		try
		{
			Console.WriteLine("Establishing connection");
			using (var streamReader = new StreamReader(await client.GetStreamAsync(url)))
			{
				while (!streamReader.EndOfStream)
				{
					var message = await streamReader.ReadLineAsync();
					Console.WriteLine($"Received price update: {message}");
				}
			}
		}
		catch(Exception ex)
		{
			//Here you can check for 
			//specific types of errors before continuing
			//Since this is a simple example, i'm always going to retry
			Console.WriteLine($"Error: {ex.Message}");
			Console.WriteLine("Retrying in 5 seconds");
			await Task.Delay(TimeSpan.FromSeconds(5));
		}
	}
}
Code language: C# (cs)

Running the console app

The console app will stay running all day. When there’s an error, it retries in 5 seconds. Instead of this very simple retry logic, consider using Polly for retries.

In the following example I didn’t have the SSE endpoint running at first, which is why it’s getting a connection error. Then I started it up and the client received multiple updates. Then I stopped the SSE endpoint, which is why it’s getting an error about the connection closing. Then I restarted it and it started receiving updates again.

Establishing connection
Error: No connection could be made because the target machine actively refused it.
Retrying in 5 seconds
Establishing connection
Received price update: Time=10/24/2022 8:05:20 AM Symbol=VTSAX Price=20.07
Received price update: Time=10/24/2022 8:05:25 AM Symbol=VTSAX Price=20.24
Received price update: Time=10/24/2022 8:05:30 AM Symbol=VTSAX Price=20.19
Received price update: Time=10/24/2022 8:05:35 AM Symbol=VTSAX Price=20.32
Received price update: Time=10/24/2022 8:05:40 AM Symbol=VTSAX Price=20.50
Error: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host...
Retrying in 5 seconds
Establishing connection
Error: No connection could be made because the target machine actively refused it
Retrying in 5 seconds
Establishing connection
Received price update: Time=10/24/2022 8:06:01 AM Symbol=VTSAX Price=20.60
Received price update: Time=10/24/2022 8:06:06 AM Symbol=VTSAX Price=20.61
Received price update: Time=10/24/2022 8:06:11 AM Symbol=VTSAX Price=21.46
Received price update: Time=10/24/2022 8:06:16 AM Symbol=VTSAX Price=20.76
Received price update: Time=10/24/2022 8:06:21 AM Symbol=VTSAX Price=21.10Code language: plaintext (plaintext)

Note: To be clear, this is calling a web API that’s returning test data with a randomized price. The price info shown is not real.

Leave a Comment