Both the client and server in a real-time app are constantly sending and receiving information. Fiddler Everywhere can provide insights on that data and its performance.
Real-time communication in applications is a great tool to provide your users with the most up-to-date information. This might take the form of a message on social media, a breaking news story or an updated stock quote. Every piece of information is urgently needed by some users.
In a real-time application, information is constantly flowing between the client and the server. This can be minuscule synchronization and keep-alive messages, or large amounts of text. To better understand what information is being exchanged, how often it is exchanged and how that affects performance, a tool like Progress Telerik Fiddler Everywhere is invaluable.
To achieve real-time communication in a web application, both the client and server need to send and receive information using the same technology. One solution is gRPC, which is a protocol on top of HTTP/2. Another solution is called WebSockets, which is its own separate protocol over a TCP connection. Microsoft also provides an abstraction called SignalR for real-time communication, which transports data using WebSockets (most of the time).
MessagePack is a super simple way to improve both WebSockets and SignalR communication. It is an “efficient binary serialization format.” In short, it encodes the plain-text JSON data traditionally sent by SignalR to binary. This will roughly halve the size of the message. Since the longest part of the real-time communication process is often transmission of data, this can greatly improve application performance. What’s even better, those encoded messages can now be natively read and decoded in Fiddler Everywhere.
The instructions below are an updated and abridged version of Use ASP.NET Core SignalR with Blazor. For a more detailed tutorial, follow the above link above.
In the server app, e.g., BlazorSignalRApp, create a Hubs folder and add ChatHubs.cs to the folder.
In Hubs/ChatHubs.cs paste the below code:
using Microsoft.AspNetCore.SignalR;
namespace BlazorSignalRApp.Hubs;
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
using Microsoft.AspNetCore.ResponseCompression;
using BlazorSignalRApp.Hubs;
builder.Services.AddSignalR();
builder.Services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
["application/octet-stream"]);
});
var app = builder.Build();
):app.UseResponseCompression();
app.Run();
):app.MapHub<ChatHub>("/chathub");
In the client app, create a razor page named Pages/Chat.razor.
Paste the following into Pages/Chat.razor:
@page "/chat"
@rendermode InteractiveWebAssembly
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager Navigation
@implements IAsyncDisposable
<PageTitle>Chat</PageTitle>
<div class="form-group">
<label>
User:
<input @bind="userInput" />
</label>
</div>
<div class="form-group">
<label>
Message:
<input @bind="messageInput" size="50" />
</label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>
<hr>
<ul id="messagesList">
@foreach (var message in messages)
{
<li>@message</li>
}
</ul>
@code {
private HubConnection? hubConnection;
private List<string> messages = new List<string>();
private string? userInput;
private string? messageInput;
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.Build();
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
var encodedMsg = $"{user}: {message}";
messages.Add(encodedMsg);
InvokeAsync(StateHasChanged);
});
await hubConnection.StartAsync();
}
private async Task Send()
{
if (hubConnection is not null)
{
await hubConnection.SendAsync("SendMessage", userInput, messageInput);
}
}
public bool IsConnected =>
hubConnection?.State == HubConnectionState.Connected;
public async ValueTask DisposeAsync()
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}
}
}
<div class="nav-item px-3">
<NavLink class="nav-link" href="chat">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Chat
</NavLink>
</div>
Now that we have a fully functional web application that uses standard plain-text SignalR, let’s add MessagePack to serialize our messages to binary.
In BOTH the server app AND the client app, add the below package:
Microsoft.AspNetCore.SignalR.Protocols.MessagePack
In the server app, update the SignalR service to include MessagePack:
services.AddSignalR()
.AddMessagePackProtocol();
var hubConnection = new HubConnectionBuilder()
.WithUrl("/chathub")
.AddMessagePackProtocol()
.Build();
To inspect SignalR traffic in Progress Telerik Fiddler Everywhere, you must first identify the WebSockets connection. SignalR will use WebSockets by default, wherever it is supported—gracefully falling back on older technologies when WebSockets are not supported. Since this is a seldom occurrence, it’s safe to assume that WebSockets are being used.
First, we should identify the URL and port of our running application and use that to filter Fiddler Everywhere traffic. We can do this even before we start listening to the traffic.
Now, we can filter Fiddler Everywhere traffic like this:
We can now launch our application and begin inspecting traffic.
Click the System Proxy switch to turn on the system network proxy a being inspecting all traffic. Everything not conforming to our filter will be discarded.
After running our application, we’ll see traffic start to appear in the Live Traffic window. All of this traffic is very informative, but we’re specifically looking for the WebSockets entry.
There are a few hints as to which item we need:
Selecting the connection, we can now use the Inspectors tab to view the Request and Response panels:
To view the messages being sent and received over SignalR (WebSockets), we’ll switch from the Handshake tab (focused on the connection) to the Messages tab (focused on the content).
You notice that for each message sent in the app, we see both a client and server response. This is because our simple chat application sends a message and receives the same message back from the SignalR Hub.
You’ll also notice that the raw content of that message is in binary (displayed in HEX values). This is MessagePack doing its job. To decode the message, open the MessagePack tab.
Fiddler Everywhere can be used during all stages of your real-time web application development lifecycle. Early in development, you may allow SignalR to send plain-text messages for easier debugging. You might also check the number of requests being sent and their size, and profile their performance metrics. In late-stage development, you can enable MessagePack and other optimization/compression protocols to increase performance. Doing so does not sacrifice your ability to read and understand the data passing through your servers or degrade your ability to inspect and profile applications in production.
If you haven’t already used Fiddler Everywhere, you can try it for free today!
Rick Hellwege is a software engineer, maker, and Principal Sales Engineer at Progress. Rick has a passion for experimenting with the latest technologies and frameworks, and constantly finding out how things work. When not writing code and driving demos, he can either be found on the most remote peaks of New England’s White Mountains, covered in sawdust in his workshop, or digging the earth in his garden.