我们使用System.Net.WebSockets编写了一个简单的WebSocket客户端。 ClientWebSocket上的KeepAliveInterval设置为30秒。
连接成功打开,流量按预期双向流动,或者如果连接空闲,客户端每30秒向服务器发送一次Pong请求(在Wireshark中可见)。
但是在100秒之后,由于TCP套接字在客户端被关闭,连接突然终止(在Wireshark中观察我们看到客户端发送FIN)。在关闭套接字之前,服务器以1001 Going Away响应。
经过大量的挖掘后,我们已经找到了原因并找到了相当苛刻的解决方法。尽管有很多Google和Stack Overflow搜索我们只看到了其他几个人发布关于这个问题的例子而没有人回答,所以我发布这个以拯救他人的痛苦,并希望有人可能能够提出更好的解决方法。
100秒超时的来源是WebSocket使用System.Net.ServicePoint,它具有MaxIdleTime属性以允许关闭空闲套接字。在打开WebSocket时,如果Uri存在现有的ServicePoint,它将使用它,无论MaxIdleTime属性在创建时设置为什么。如果没有,将创建一个新的ServicePoint实例,MaxIdleTime根据System.Net.ServicePointManager MaxServicePointIdleTime属性的当前值设置(默认为100,000毫秒)。
问题是,就ServicePoint空闲计时器而言,WebSocket流量和WebSocket保持活动(Ping / Pong)似乎都不会注册为流量。因此,在打开WebSocket后100秒,它就会被拆除,尽管有交通或保持活动。
我们的预感是,这可能是因为WebSocket作为HTTP请求启动,然后升级到websocket。似乎空闲计时器仅查找HTTP流量。如果确实发生了这似乎是System.Net.WebSockets实现中的一个主要错误。
我们使用的解决方法是将ServicePoint上的MaxIdleTime设置为int.MaxValue。这允许WebSocket无限期保持打开状态。但缺点是该值适用于该ServicePoint的任何其他连接。在我们的上下文(使用Visual Studio Web和负载测试进行负载测试)中,我们为同一个ServicePoint打开了其他(HTTP)连接,实际上在我们打开WebSocket时已经存在一个活动的ServicePoint实例。这意味着在我们更新MaxIdleTime之后,Load测试的所有HTTP连接都没有空闲超时。虽然实际上Web服务器应该关闭空闲连接,但这并不是很舒服。
我们还简要探讨是否可以创建一个仅为我们的WebSocket连接保留的新ServicePoint实例,但是看不到这样做的干净方法。
另一个让人难以追踪的小麻烦是虽然System.Net.ServicePointManager MaxServicePointIdleTime属性默认为100秒,但Visual Studio会覆盖此值并将其设置为120秒 - 这使得搜索更难
答案 0 :(得分:9)
本周我遇到了这个问题。你的解决方法让我指出了正确的方向,但我相信我已经缩小了根本原因。
如果来自WebSocket服务器的“101 Switching Protocols”响应中包含“Content-Length:0”标头,则WebSocketClient会混淆并在100秒内安排连接以进行清理。
以下是来自.Net Reference Source的违规代码:
//if the returned contentlength is zero, preemptively invoke calldone on the stream.
//this will wake up any pending reads.
if (m_ContentLength == 0 && m_ConnectStream is ConnectStream) {
((ConnectStream)m_ConnectStream).CallDone();
}
根据RFC 7230第3.3.2节,1xx(信息)消息中禁止使用Content-Length,但我发现它错误地包含在某些服务器实现中。
有关其他详细信息,包括用于诊断ServicePoint问题的一些示例代码,请参阅此主题:https://github.com/ably/ably-dotnet/issues/107
答案 1 :(得分:0)
我将套接字的KeepAliveInterval设置为0,如下所示:
theSocket.Options.KeepAliveInterval = TimeSpan.Zero;
这消除了在达到超时时websocket关闭的问题。但是话又说回来,它也可能完全关闭了ping消息的发送。