【问题标题】:web socket connection closed when behind proxyWeb 套接字连接在代理后面时关闭
【发布时间】:2016-02-18 22:22:25
【问题描述】:

我有一个基于网络套接字的聊天应用程序 (HTML5)。

浏览器通过 wss 打开与基于 Java 的 Web 套接字服务器的套接字连接。

当浏览器直接连接到服务器(没有任何代理)时,一切正常。

但是当浏览器位于企业代理之后,浏览器套接字连接会在大约 2 分钟无活动后自动关闭。 浏览器控制台显示“Socket closed”。

在我的测试环境中,我有一个 Squid-Dansguardian 代理服务器。

IMP:如果浏览器在没有任何代理的情况下连接,则不会观察到此行为。

为了让一些活动继续进行,我嵌入了一个简单的 jquery 脚本,它将每 60 秒向另一台服务器发出一个 http GET 请求。但这并没有帮助。大约 2 分钟无操作后,我的浏览器控制台中仍然出现“套接字关闭”。

欢迎任何帮助或指点。

谢谢

【问题讨论】:

  • 代理是否支持或过滤掉 wss: 协议?我知道我们的公司代理不知道如何处理它,所以流量没有通过。
  • 你需要一个回退机制,所以当 wss 没有通过时,你可以回退到长轮询或其他东西,这需要在服务器和客户端上工作。
  • 快速说明:如果有持续的聊天活动,则套接字不会关闭。只有在大约 2 分钟内没有任何活动(聊天消息)时,Socket 才会自动关闭。
  • 服务器每分钟发送 WebSocket ping 帧怎么样?这应该可以保持连接活跃。

标签: javascript html websocket http-proxy


【解决方案1】:

在我看来这是一个功能,而不是错误

在生产应用程序中存在与所谓的“半开”套接字相关的问题 - see this great blog post about it

会发生连接突然丢失,导致 TCP/IP 连接在未通知连接另一方的情况下断开。发生这种情况的原因有很多 - wifi 信号或蜂窝信号丢失、路由器崩溃、调制解调器断开连接、电池没电、停电......

检测套接字是否真正打开的唯一方法是尝试发送数据...但是,您的代理可能无法在不干扰应用程序逻辑的情况下安全地发送数据*

两分钟后,您的代理假定连接丢失并关闭其末端的套接字以节省资源并允许建立新连接。

如果您的代理没有采取这种预防措施,那么在足够长的时间内,您的所有可用资源都将被永远不会关闭的断开连接占用,从而阻止访问您的应用程序

两分钟很多。在 Heroku 上,他们将代理设置为 50 秒(更合理)。对于 Http 连接,这些超时通常要短得多。

对您来说最好的选择是在 2 分钟的时间内继续发送 websocket 数据。

Websocket 协议通过实现内部 ping 机制解决了这个问题 - 使用它。这些 ping 应该由服务器发送,浏览器直接用 pong 响应它们(不涉及 javascript 应用程序)。

Javascript API(至少在浏览器上)不允许您发送 ping 帧(我猜这是一个安全问题,可以防止人们使用浏览器进行 DoS 攻击)。

一些开发人员的常见做法(我认为这是错误的)是实现一个 JSON ping 消息,该消息要么被服务器忽略,要么导致 JSON pong。

由于您在服务器上使用 Java,您可以访问 Ping 机制,我建议您实现它。

我还建议(如果您可以控制代理)将超时降低到更合理的 50 秒限制


* 生产过程中的情况其实更糟……

因为中间有很长的链(家庭路由器/调制解调器、NAT、ISP、网关、路由器、负载均衡器、代理...),您的应用程序很可能可以成功发送数据,因为它仍然“连接”到中间人之一。

这应该会启动一个连锁反应,该连锁反应只会在一段时间后到达应用程序,并且只有在它尝试发送数据时才会再次发生。

这就是为什么 Ping 帧期望 Pong 帧被返回(意味着连接链是完整的。

附言

您可能还应该抱怨 Java 应用程序在特定超时后没有关闭连接。在生产过程中,这种疏忽可能会迫使您每隔一段时间重新启动服务器或遇到 DoS 情况(所有可用的文件句柄都将用于不活动的旧连接,您将没有空间容纳新连接)。

【讨论】:

    【解决方案2】:

    检查 squid.conf 中的 request_timeout 值。您可以通过 request_timeout 更改此设置。这不仅会影响 Web 套接字。例如,在我经常工作的环境中,使用 perl 脚本来生成各种配置。执行可能需要 5-10 分钟以上才能完成。我们的 httpd 和 squid 服务器上的超时值都必须提高以弥补这一点。

    此外,还要查看 connect_timeout 值。默认为一分钟..

    【讨论】:

    • 我的 squid.conf 没有定义 request_timeout。都没有connect_timeout。再想一想,就我的情况而言,依赖 squid 参数可能不是一个好主意,因为我无法控制客户端的代理设置和配置。所以我可能需要在套接字中发送一些虚拟请求以使其保持活动状态。
    • 如果没有定义超时,那么 request_tmeout 的默认值为 2 分钟,connect_timeout 的默认值为 1 分钟...我会提高 request_timeout 。此外,正如您所提到的,实现 ping/pong 类型的场景是一个好主意。在我的一个 Web 套接字实现中,我使用 RFC6455 本质上强制要求的 ping/pong 方法来做到这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-22
    • 1970-01-01
    • 2015-01-08
    相关资源
    最近更新 更多