I'm doing SignalR server-to-client streaming using System.Threading.Channel
with a .NET client. The usage is fairly basic, similar to what is described in the introductory docs.
The hub code is similar to this:
public ChannelReader<byte[]> Retrieve(Guid id, CancellationToken cancellationToken)
{
var channel = Channel.CreateBounded<byte[]>(_limit);
_ = WriteItemsAsync(channel.Writer, id, cancellationToken);
return channel.Reader;
}
private async Task WriteItemsAsync(ChannelWriter<byte[]> writer, Guid id, CancellationToken cancellationToken)
{
Exception localException = null;
try
{
//loop and write to the ChannelWriter until finished
}
catch (Exception ex)
{
localException = ex;
}
finally
{
writer.Complete(localException);
}
}
And client similar to this:
var channel = await hubConnection.StreamAsChannelAsync<byte[]>("Retrieve", _guid, cancellationTokenSource.Token);
while (await channel.WaitToReadAsync())
{
while (channel.TryRead(out var data))
{
//handle data
}
}
When my hub method is done streaming, it calls Complete()
on its ChannelWriter
. SignalR, presumably, internally sees the Complete
call on the corresponding ChannelReader
, translates that into an internal SignalR message and delivers that to the client. The client's own ChannelReader
is then marked as complete by SignalR and my client code wraps up its own work on the stream.
Is that "completed" notification, from server to client, guaranteed to be delivered? In other cases where the hub is broadcasting non-streaming messages to clients, it generally "fires and forgets", but I've got to assume that calling Complete
on the streaming Channel
has acknowledged delivery, otherwise the client could be in a state where it is holding a streaming ChannelReader
open indefinitely while the server sees the stream as closed.
Less important to the question, but the reason I ask is that I am trying to narrow down just such a scenario where a data flow pipeline that consumes a SignalR streaming interface very occasionally hangs, and it seems that the only point where it is hanging is somewhere in the SignalR client.
I asked the devs over on github: https://github.com/dotnet/aspnetcore/issues/30128.
Here is a quick summary:
"Messages over SignalR are as reliable as TCP. Basically what this means is that either you get the message, or the connection closes. In both cases the channel on the client side will be completed."
And:
"Right it should never hang forever. It'll either send the complete message or disconnect. Either way, a hang would be a bug in the client"