Using Azure Web PubSub with Protobuf subprotocol in .NET
Tags: azure, webpubsub, protobuf, csharp
Published: 2023-07-27 11:33:29

[Azure Web PubSub](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/overview) is a fully managed service that helps developers add real-time features using WebSockets and the publish-subscribe pattern.
It supports the publish-subscribe messaging pattern and is scalable, reliable, and secure.
Example of use are:

- Live dashboards and monitoring.
- Cross-platform live chat.
- Push instant notifications.
- Real-time broadcasting.

### Prerequisites
- An [Azure subscription](https://azure.microsoft.com/free/dotnet/).   
- An existing Web PubSub instance. [Create Web PubSub instance](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-develop-create-instance?tabs=CLI&pivots=method-azure-portal)

### Getting started  

In order to connect to the Web PubSub service, you need to generate a connection string, the easiest way is to use the Azure portal, navigate to your Web PubSub instance, select the **Keys** entry under **Settings** on the left panel, scroll down the page to the **client URL generator**, configure it and copy the the **Client Access URL**.
##### Note
For better and safer alternatives to generate the connection string, please refer to the [official documentation](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-generate-client-access-url?tabs=javascript).

From this point on all you need to communicate with the Web PubSub service is to open a websocket connection to the generated URL and send/receive messages using the proper subprotocol ([example](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/concept-client-protocols)) but this is quite tedious and assumes a deep knowledge of the Web PubSub subprotocols.

Fortunately, thank to [dedicated SDKs](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/quickstarts-pubsub-among-clients), using the service does not require to deal with the subprotocols directly.

### A simple .NET Core example

Let's create a simple console application that connects to the Web PubSub service and sends a message to a group.

 1 - Create a new .NET Core console application.
 ```dotnetcli
 dotnet new console
 ```
 2 - Add the [Azure.Messaging.WebPubSub](https://www.nuget.org/packages/Azure.Messaging.WebPubSub) NuGet package to the project.
```dotnetcli
dotnet add package Azure.Messaging.WebPubSub.Client --prerelease
```
3 - Add the following using statements to the `Program.cs` file.
```csharp
using Azure.Messaging.WebPubSub.Clients;
```
4 - Insert the following code in the `Main` method.
```csharp
const string Group = "protobuf-client-group";
const string Uri = "<your-connection-string>";

//Create the client
var serviceClient = new WebPubSubClient(new Uri(Uri));

//Subscribe to events
serviceClient.Connected += (arg) =>
{
    Console.WriteLine($"Connected with connection id: {arg.ConnectionId}");
    return Task.CompletedTask;
};
serviceClient.Disconnected += (arg) =>
{
    Console.WriteLine($"Disconnected from connection id: {arg.ConnectionId}");
    return Task.CompletedTask;
};

serviceClient.GroupMessageReceived += (arg) =>
{
    Console.WriteLine($"Received text message: {arg.Message.Data}");
    return Task.CompletedTask;
};

//Connects and join the group
await serviceClient.StartAsync();
await serviceClient.JoinGroupAsync(Group);

while (true)
{
    Console.WriteLine("Enter the message to send or just enter to stop");
    var message = Console.ReadLine();

    //Send a message to the group
    if (!string.IsNullOrEmpty(message))
    {
        await serviceClient.SendToGroupAsync(Group, BinaryData.FromString(message), WebPubSubDataType.Text);
    }
    else
    {
        // Disconnects and leave the group
        await serviceClient.LeaveGroupAsync(Group);
        await serviceClient.StopAsync();
        break;
    }
}
```
5 - Run the application and enter a message to send to the group, you should see the message echoed back, you can also run more instances of the application to see the messages broadcasted to all the clients in the group.

### JSON vs Protobuf  

While being both data serialization protocols, as you can read [here](https://auth0.com/blog/beating-json-performance-with-protobuf/), Protobuf is around 5 times faster and around 33% the size of the same JSON message.
Considering that the max size of a Web PubSub message is [1 MB](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/key-concepts) choosing Protobuf instead of JSON can be a winning choice in terms of speed and compactness of the messages exchanged.

### Using the Protobuf subprotocol
This previous example uses the `json.webpubsub.azure.v1` subprotocol meaning that all the messages are sent and received as JSON objects, in case of binary data it gets encoded as base64 string.
The library also supports the `json.reliable.webpubsub.azure.v1` subprotocol, to use it all you need to do is to specify the subprotocol when creating the client:
```csharp
var client = new WebPubSubClient(new Uri(Uri), new WebPubSubClientOptions
{
    Protocol = new WebPubSubJsonReliableProtocol()
});
```

But what if instead i want to use the `protobuf.webpubsub.azure.v1` and `protobuf.reliable.webpubsub.azure.v1` subprotocols supported by the Web PubSub?  

Unfortunately the library does not support them out of the box but thank to this [NuGet package] (https://www.nuget.org/packages/WebPubSub.Protobuf) you can easily add support for them.   

### Using the protobuf subprotocols
1 - Add the [WebPubSub.Protobuf](https://www.nuget.org/packages/Azure.Messaging.WebPubSub) NuGet package to the project
```dotnetcli
dotnet add package WebPubSub.Protobuf --version 1.0.0
```
2 - Add the following using statements to the `Program.cs` file
```csharp
using WebPubSub.Client.Protobuf
```
3 - Add the protobuf subprotocol when creating the client
```csharp

var client = new WebPubSubClient(new Uri("<client-access-uri>"), new WebPubSubClientOptions
{
    Protocol = new WebPubSubProtobufProtocol() // or new WebPubSubProtobufReliableProtocol()
});
```
4 - Run the application and see that it behaves exactly as before but now the messages are sent and received as protobuf messages.

### Sending custom Protobuf serialized messages
The source code of the NuGet package is available [here](https://github.com/corradocavalli/WebPubSub.Protobuf) and it includes a sample that shows how to send and receive messages serialized in `Text`, `JSON`, `Binary` and `Protobuf`.

### References
- [Web PubSub service](https://azure.microsoft.com/en-us/services/web-pubsub/)
- [Web PubSub service documentation](https://docs.microsoft.com/en-us/azure/azure-web-pubsub/overview)
- [Web PubSub service client protocols](https://docs.microsoft.com/en-us/azure/azure-web-pubsub/concept-client-protocols)
- [Web PubSub service client SDKs](https://docs.microsoft.com/en-us/azure/azure-web-pubsub/quickstarts-pubsub-among-clients)
- [Web PubSub service client SDK for .NET](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Messaging.WebPubSub_1.3.0/sdk/webpubsub/Azure.Messaging.WebPubSub.Client)
- [Create Protobuf messages for .NET apps](https://learn.microsoft.com/en-us/aspnet/core/grpc/protobuf?view=aspnetcore-6.0)
- [Protocol Buffers/gRPC Codegen Integration Into .NET Build](https://chromium.googlesource.com/external/github.com/grpc/grpc/+/HEAD/src/csharp/BUILD-INTEGRATION.md)