jjzjj

c# - .NET WebSockets 强行关闭,尽管连接保持事件状态

coder 2024-05-21 原文

我们已经使用 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 实例,并根据 System.Net.ServicePointManager MaxServicePointIdleTime 属性的当前值(默认为 100,000 毫秒)设置 MaxIdleTime。

问题是,就 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 之后,负载测试的所有 HTTP 连接都将没有空闲超时。这让人感觉不太舒服,尽管实际上网络服务器无论如何都应该关闭空闲连接。

我们还简要探讨了我们是否可以创建一个新的 ServicePoint 实例,只为我们的 WebSocket 连接保留,但没有找到一种干净的方法。

另一个让这个更难追踪的小问题是,虽然 System.Net.ServicePointManager MaxServicePointIdleTime 属性默认为 100 秒,但 Visual Studio 覆盖了这个值并将其设置为 120 秒 - 这使得搜索更难.

最佳答案

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

如果来自 WebSocket 服务器的“101 切换协议(protocol)”响应中包含“Content-Length:0” header ,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 节,Content-Length 在 1xx(信息性)消息中被禁止,但我发现它被错误地包含在某些服务器实现中。

有关其他详细信息,包括一些用于诊断 ServicePoint 问题的示例代码,请参阅此线程:https://github.com/ably/ably-dotnet/issues/107

关于c# - .NET WebSockets 强行关闭,尽管连接保持事件状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40502921/

有关c# - .NET WebSockets 强行关闭,尽管连接保持事件状态的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  3. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

  4. ruby - 无法在 60 秒内获得稳定的 Firefox 连接 (127.0.0.1 :7055) - 2

    我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类

  5. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  6. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  7. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

  8. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  9. ruby - 如何关闭 ruby​​ gem "Spreadsheet?"中的文件 - 2

    下面的代码在我第一次运行它时就可以正常工作:require'rubygems'require'spreadsheet'book=Spreadsheet.open'/Users/me/myruby/Mywks.xls'sheet=book.worksheet0row=sheet.row(1)putsrow[1]book.write'/Users/me/myruby/Mywks.xls'当我再次运行它时,我会收到更多消息,例如:/Library/Ruby/Gems/1.8/gems/spreadsheet-0.6.5.9/lib/spreadsheet/excel/reader.rb:11

  10. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

随机推荐