Exercise 2: Server-Side Streaming - Live Temperature Updates

Exercise 2: Server-Side Streaming - Live Temperature Updates

๐Ÿงช Exercise 2: Server-Side Streaming - Live Temperature Updates

In our first exercise, we explored a simple unary gRPC call. Now, we'll dive into one of gRPC's most powerful features: **server-side streaming**. This is perfect for scenarios where a single client request results in a continuous stream of data from the server, such as live data feeds or real-time updates.

In this exercise, you will modify your existing gRPC server to continuously send mock temperature updates for a specified city to your Blazor client.


✅ Step 1: Update `.proto` in gRPC Server

We need a new service definition for our weather streamer. In your `GrpcServerDemo` project, create a new file named `Protos/weather.proto`.


// Protos/weather.proto
syntax = "proto3";

option csharp_namespace = "GrpcServerDemo";

// Defines a service for streaming weather data
service WeatherStreamer {
  // A single request returns a stream of WeatherReply messages
  rpc StreamWeather(WeatherRequest) returns (stream WeatherReply);
}

// Message sent from the client to the server
message WeatherRequest {
  string city = 1;
}

// Message sent from the server to the client
message WeatherReply {
  string city = 1;
  float temperature = 2;
  string timestamp = 3;
}
        

The key change here is the `returns (stream WeatherReply)` part. This tells the gRPC framework that the server will be sending back multiple messages in response to a single `WeatherRequest`.

Next, update your `GrpcServerDemo.csproj` file to include the new `.proto` file and generate server-side code.


<ItemGroup>
  <Protobuf Include="Protos\greeter.proto" GrpcServices="Server" />
  <Protobuf Include="Protos\weather.proto" GrpcServices="Server" /> <!-- Add this line -->
</ItemGroup>
        

✅ Step 2: Implement Server Streaming Service

Now, create the implementation for the new `WeatherStreamer` service in a new file named `Services/WeatherStreamerService.cs`.


// Services/WeatherStreamerService.cs
using Grpc.Core;
using GrpcServerDemo;

public class WeatherStreamerService : WeatherStreamer.WeatherStreamerBase
{
    public override async Task StreamWeather(WeatherRequest request, IServerStreamWriter<WeatherReply> responseStream, ServerCallContext context)
    {
        var rand = new Random();

        // Loop to simulate sending a stream of updates
        for (int i = 0; i < 10; i++)
        {
            // Check for client cancellation
            if (context.CancellationToken.IsCancellationRequested)
            {
                break;
            }

            // Create a new weather update message
            var reply = new WeatherReply
            {
                City = request.City,
                Temperature = (float)(20 + rand.NextDouble() * 10), // Generate a random temperature
                Timestamp = DateTime.Now.ToString("HH:mm:ss")
            };

            // Write the message to the response stream
            await responseStream.WriteAsync(reply);
            
            // Wait for 2 seconds before sending the next update
            await Task.Delay(2000);
        }
    }
}
        

The `StreamWeather` method receives an `IServerStreamWriter` argument. This is the key to server streaming; you use this writer to send multiple messages back to the client. The `Task.Delay` simulates a real-world delay between data updates.

Next, update `Program.cs` in the `GrpcServerDemo` project to map your new service.


var builder = WebApplication.CreateBuilder(args);

builder.Services.AddGrpc();

var app = builder.Build();

app.MapGrpcService<GreeterService>();
app.MapGrpcService<WeatherStreamerService>(); <!-- Map the new service -->
app.MapGet("/", () => "Use a gRPC client to communicate.");

app.Run();
        

✅ Step 3: Blazor Client - Stream Weather Data

Now, in your `BlazorGrpcClient` project, you'll need to update the `.csproj` file to generate client-side code for the `weather.proto` file.


<ItemGroup>
  <Protobuf Include="Protos\greeter.proto" GrpcServices="Client" />
  <Protobuf Include="Protos\weather.proto" GrpcServices="Client" /> <!-- Add this line -->
</ItemGroup>
        

Next, you'll need to configure the gRPC client for the new `WeatherStreamer` service in `BlazorGrpcClient`'s `Program.cs`.


builder.Services.AddSingleton(services =>
{
    var channel = GrpcChannel.ForAddress("https://localhost:7001");
    // Add both clients here
    return new Greeter.GreeterClient(channel);
});
builder.Services.AddSingleton(services =>
{
    var channel = GrpcChannel.ForAddress("https://localhost:7001");
    return new WeatherStreamer.WeatherStreamerClient(channel);
});
        

Finally, create a new Razor component `Pages/WeatherStream.razor` to display the incoming data.


@page "/weather"
@inject GrpcServerDemo.WeatherStreamer.WeatherStreamerClient WeatherClient

<h3>Weather Streaming</h3>
<p>Real-time temperature updates from the server.</p>

<div class="input-group mb-3">
    <input type="text" class="form-control" @bind="City" placeholder="Enter city">
    <button class="btn btn-success" type="button" @onclick="StartStreaming">Start Streaming</button>
</div>

<h4>Live Updates:</h4>
<ul class="list-unstyled">
    @foreach (var update in Updates)
    {
        <li><code>@update</code></li>
    }
</ul>

@code {
    private string City { get; set; } = "New York";
    private List<string> Updates { get; set; } = new();

    private async Task StartStreaming()
    {
        Updates.Clear(); // Clear previous updates
        try
        {
            // Make the server streaming call
            var call = WeatherClient.StreamWeather(new WeatherRequest { City = City });

            // Read the response stream asynchronously
            await foreach (var response in call.ResponseStream.ReadAllAsync())
            {
                Updates.Add($"{response.Timestamp} | {response.City}: {response.Temperature:F2} °C");
                StateHasChanged(); // Force the UI to update with each new message
            }
        }
        catch (RpcException ex)
        {
            Updates.Add($"Error: {ex.Status.Detail}");
            StateHasChanged();
        }
    }
}
        

Run both projects. When you navigate to `/weather` in your Blazor app and click "Start Streaming," you will see a new temperature update appear every two seconds, demonstrating the power of server-side streaming in gRPC.

Raushan Ranjan

Microsoft Certified Trainer

.NET | Azure | Power Platform | WPF | Qt/QML Developer

Power BI Developer | Data Analyst

๐Ÿ“ž +91 82858 62455
๐ŸŒ raushanranjan.azurewebsites.net
๐Ÿ”— linkedin.com/in/raushanranjan

Comments

Popular posts from this blog

Blazor WebAssembly Hosted App Tutorial: Envelope Tracker System

Authentication and Authorization in Blazor

Securing MVC-based Applications Using Blazor