WebSocket 单工流 - 客户端如何关闭连接?
WebSocket simplex stream - how can the client close the connection?
我正在使用 WebSockets 将值从服务器流式传输到客户端。当值流完成(服务器端终止)或客户端停止订阅(客户端终止)时,应关闭连接。
客户端如何优雅的关闭连接?
演示 AspNetCore 问题的粗略示例;服务器推送(可能)无限的价值流;客户端订阅前 5 个值,然后应该关闭连接。
app.Use(async (context, next) =>
{
if (context.Request.Path == "/read")
{
var client = new ClientWebSocket();
await client.ConnectAsync(new Uri(@"wss://localhost:7125/write"), CancellationToken.None);
for (int i = 0; i < 5; i++)
await client.ReceiveAsync(new ArraySegment<byte>(new byte[sizeof(int)]), CancellationToken.None);
// This does not seem to have any particular effect on writer
// await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
// This freezes in AspNetcore on .NET6 core (because the implementation waits for the connection to close, which never happens)
// In AspNet (non-core, .Net Framework 4.8), this seems to throw an exception that data cannot be read after the connection has been closed (i.e. the socket seems to only be closeable if no data is pending to be read)
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
if (context.Request.Path == "/write")
{
var ws = await context.WebSockets.AcceptWebSocketAsync();
await foreach (var number in GetNumbers())
{
var bytes = BitConverter.GetBytes(number);
if (ws.State != WebSocketState.Open)
throw new Exception("I wish we'd hit this branch, but we never do!");
await ws.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Binary, true, CancellationToken.None);
}
}
});
static async IAsyncEnumerable<int> GetNumbers()
{
for (int i = 0; i <= int.MaxValue; i++)
{
yield return i;
await Task.Delay(25);
}
}
一般问题似乎是 /write 方法未获取关闭消息,即 ws.State 仍然是 WebSocketState.Open。我假设只有接收操作才能更新连接状态?
有没有什么好的方法可以处理这种情况/让服务器接收到客户端关闭连接的请求?
我很想避免客户端必须向服务器发送任何显式消息/服务器必须显式读取流。不过,我越来越想知道这是否可能。
WebSocket 协议的工作方式类似于 TCP - 通过连接建立,唯一的区别 - 启动是通过 http[s] 完成的。
来自一侧的一个发送动作与来自另一侧的一个接收动作相匹配,反之亦然。
你可以在 documentation:
的评论中注意到这个细节(如果我没记错的话)
Exactly one send and one receive is supported on each WebSocket object in parallel.
因此,您应该至少收到一个数据段并识别来自客户端的CloseIntention 消息。客户端也一样。
如何接收消息、识别密切意图并对其做出正确反应 - 请参阅 here。
如何发送关闭意向消息 - 请参阅 here。
怀疑您应该在服务器后台至少调用一次 webSocket.ReceiveAsync
。
然后,在 ContinueWith
任务中为当前服务器套接字会话调用 CancellationTokenSource.Cancel()。
除了 docker-compose 之外,该回购协议正在运行。 - 我在复杂的 DevOps 方面有点新手。 )
更新
文档的备注部分不是关于在对话的不同方面匹配发送-接收操作。只是想让您注意这个 TCP 概念是如何工作的,即您应该至少接收一次数据。
我正在使用 WebSockets 将值从服务器流式传输到客户端。当值流完成(服务器端终止)或客户端停止订阅(客户端终止)时,应关闭连接。
客户端如何优雅的关闭连接?
演示 AspNetCore 问题的粗略示例;服务器推送(可能)无限的价值流;客户端订阅前 5 个值,然后应该关闭连接。
app.Use(async (context, next) =>
{
if (context.Request.Path == "/read")
{
var client = new ClientWebSocket();
await client.ConnectAsync(new Uri(@"wss://localhost:7125/write"), CancellationToken.None);
for (int i = 0; i < 5; i++)
await client.ReceiveAsync(new ArraySegment<byte>(new byte[sizeof(int)]), CancellationToken.None);
// This does not seem to have any particular effect on writer
// await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
// This freezes in AspNetcore on .NET6 core (because the implementation waits for the connection to close, which never happens)
// In AspNet (non-core, .Net Framework 4.8), this seems to throw an exception that data cannot be read after the connection has been closed (i.e. the socket seems to only be closeable if no data is pending to be read)
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
if (context.Request.Path == "/write")
{
var ws = await context.WebSockets.AcceptWebSocketAsync();
await foreach (var number in GetNumbers())
{
var bytes = BitConverter.GetBytes(number);
if (ws.State != WebSocketState.Open)
throw new Exception("I wish we'd hit this branch, but we never do!");
await ws.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Binary, true, CancellationToken.None);
}
}
});
static async IAsyncEnumerable<int> GetNumbers()
{
for (int i = 0; i <= int.MaxValue; i++)
{
yield return i;
await Task.Delay(25);
}
}
一般问题似乎是 /write 方法未获取关闭消息,即 ws.State 仍然是 WebSocketState.Open。我假设只有接收操作才能更新连接状态?
有没有什么好的方法可以处理这种情况/让服务器接收到客户端关闭连接的请求?
我很想避免客户端必须向服务器发送任何显式消息/服务器必须显式读取流。不过,我越来越想知道这是否可能。
WebSocket 协议的工作方式类似于 TCP - 通过连接建立,唯一的区别 - 启动是通过 http[s] 完成的。
来自一侧的一个发送动作与来自另一侧的一个接收动作相匹配,反之亦然。
你可以在 documentation:
Exactly one send and one receive is supported on each WebSocket object in parallel.
因此,您应该至少收到一个数据段并识别来自客户端的CloseIntention 消息。客户端也一样。
如何接收消息、识别密切意图并对其做出正确反应 - 请参阅 here。
如何发送关闭意向消息 - 请参阅 here。
怀疑您应该在服务器后台至少调用一次 webSocket.ReceiveAsync
。
然后,在 ContinueWith
任务中为当前服务器套接字会话调用 CancellationTokenSource.Cancel()。
除了 docker-compose 之外,该回购协议正在运行。 - 我在复杂的 DevOps 方面有点新手。 )
更新
文档的备注部分不是关于在对话的不同方面匹配发送-接收操作。只是想让您注意这个 TCP 概念是如何工作的,即您应该至少接收一次数据。