我正在使用带有 .NET 客户端的 System.Threading.Channel
进行 SignalR 服务器到客户端流式传输。用法相当基本,类似于介绍性 docs 中描述的内容。
集线器代码类似于:
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);
}
}
和客户端类似:
var channel = await hubConnection.StreamAsChannelAsync<byte[]>("Retrieve", _guid, cancellationTokenSource.Token);
while (await channel.WaitToReadAsync())
{
while (channel.TryRead(out var data))
{
//handle data
}
}
当我的集线器方法完成流式传输时,它会在其 Complete()
上调用 ChannelWriter
。 SignalR 大概在内部看到相应 Complete
上的 ChannelReader
调用,将其转换为内部 SignalR 消息并将其传递给客户端。然后,客户端自己的 ChannelReader
被 SignalR 标记为完成,我的客户端代码在流上完成自己的工作。
从服务器到客户端的“完成”通知是否可以保证被传递?在其他情况下,集线器向客户端广播非流消息,它通常“触发并忘记”,但我必须假设在流 Complete
上调用 Channel
已确认交付,否则客户端可能处于一种状态,即它在服务器的同时无限期地保持流 ChannelReader
打开将流视为关闭。
对这个问题不太重要,但我问的原因是我试图缩小这种情况,即消耗 SignalR 流接口的数据流管道偶尔会挂起,而且似乎是唯一的点挂在 SignalR 客户端的某个地方。
答案 0 :(得分:1)
我在 github 上询问了开发人员:https://github.com/dotnet/aspnetcore/issues/30128。
这里是一个快速总结:
“SignalR 上的消息与 TCP 一样可靠。基本上这意味着要么您收到消息,要么连接关闭。在这两种情况下,客户端的通道都将完成。”
还有:
“没错,它不应该永远挂起。它要么发送完整的消息,要么断开连接。无论哪种方式,挂起都会是客户端的错误”