尽管在连接上保持活动和活动,.NET WebSockets仍强制关闭

我们使用System.Net.WebSockets编写了一个简单的WebSocket客户端。 ClientWebSocket上的KeepAliveInterval设置为30秒。

连接成功打开,流量按预期在两个方向上流动,或者如果连接空闲,客户端每30秒向服务器发送一次Pong请求(在Wireshark中可见)。

但是在100秒之后,由于TCP套接字在客户端被关闭,连接突然终止(在Wireshark中观察我们看到客户端发送FIN)。 在关闭套接字之前,服务器以1001 Going Away响应。

经过大量的挖掘,我们已经找到了原因,并找到了一个相当沉重的解决方法。 尽管有很多谷歌和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秒 – 这使得搜索更加困难。

我本周遇到了这个问题。 你的解决方法让我指出了正确的方向,但我相信我已经缩小了根本原因。

如果来自WebSocket服务器的“101 Switching Protocols”响应中包含“Content-Length:0”标头,则WebSocketClient会混淆并在100秒内安排连接以进行清理。

以下是.Net参考资料来源的违规代码:

//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