WebSockets的ping/pong,为什么不使用TCP keepalive?

112

WebSockets有选择发送ping到另一端的选项,另一端应该以pong响应。

收到Ping帧后,端点必须发送Pong帧作为响应,除非它已经收到Close帧。 它应该尽快以Pong帧作出响应。

TCP 以keepalive形式提供类似功能:

你可以发送一个没有数据、开启ACK标志位的keepalive探针包给对端。由于TCP/IP规范,你可以这样做,作为一种重复的ACK,并且远程终端没有任何参数,因为TCP是面向流的协议。另一方面,你将从远程主机(它不需要支持keepalive,只需要支持TCP/IP)接收到一个带有没有数据和设置了ACK的回复。

我认为TCP keepalive更有效,因为它可以在内核中处理而无需将数据传输到用户空间,解析websocket帧,制作响应帧并将其交还内核进行传输。这也减少了网络流量。

此外,WebSocket被明确指定始终在TCP上运行;它们不是传输层不可知的,因此TCP keepalive始终可用:

WebSocket协议是一种独立的基于TCP的协议。

那么,为什么要使用WebSocket ping/pong而不是TCP keepalive呢?


2
实际上,人们从来不使用WebSocket ping/pong,因为没有创建API。而且,人们也从不使用TCP keepalive,原因在答案中已经说明。这是一个很好的例子,说明分层引入了复杂性,却没有解决问题:每个层都必须实现相同的功能,但每个层都有自己的无用原因。因此,应用程序仍然必须在所有其他层之上实现自己的keepalive。 - personal_cloud
没错,我正在使用类似于新层的STOMP,并且STOMP有它自己的ping/pong。 - John Xiao
也许他们正试图为其他交通方式留下一扇半开的大门(将来可能会用到/以防万一),例如一个不可知论的(/全面可控的)乒乓机制。 - DennisVM-D2i
添加一条新评论,因为我没有时间编辑我的上一条评论:(但当我使用一个库的'IsAlive'属性/检查时,乒乓球并不那么有趣,我尝试在我的Ping处理程序中显示它的值 - 与我还想评估值的其他属性一起,结果发现'IsAlive'调用了'(Send)Ping()'方法/函数。哎呀!客户端和服务器永无止境的乒乓战争!!;P) - DennisVM-D2i
5个回答

110
TCP keepalive存在的问题如下:
  1. TCP keepalive默认是关闭的。
  2. TCP keepalive默认以两小时为间隔运作,而不像Ping/Pong协议那样根据需要执行。
  3. TCP keepalive在代理之间运作,而不是端到端。
  4. 正如@DavidSchwartz所指出的那样,TCP keepalive在TCP堆栈之间运作,而不是在应用程序之间运作,因此它不能告诉我们应用程序是否还在运行。
与WebSockets ping/pong的比较没有意义。TCP keepalive是自动的,并且有定时器,当启用时会按计划执行,而WebSocket ping/pong则根据应用程序的要求执行。

2
您可以使用 setsockopt(2) 在每个连接的基础上更改这两个设置。 - Thomas
4
您可以在一些平台上更改时间间隔。但是不能在Windows、FreeBSD、Solaris等平台上更改。 - user207421
32
它做了错误的事情。我们想知道的是另一端的应用程序是否存活。正如你所提到的,TCP keepalive 在内核中处理,它们无法告诉我们另一端的应用程序是否存活。 - David Schwartz
1
@DavidSchwartz 我认为这是最重要的原因。也许可以把它转化为一个答案? - Ashkan Kh. Nazary
1
@David 分层不应该用来解决内核或应用程序中的错误(尽管,可悲的是,我经常看到这种情况)。TCP keepalives 应该在套接字关闭时停止。内核应该在应用程序退出时关闭套接字。应用程序应该在停止工作时退出(例如,它们可以包括一个看门狗线程)。如果其中任何一个没有发生,那么这就是内核或应用程序中的错误。修复错误;不要在其上添加层。 - personal_cloud
显示剩余4条评论

51
除了 EJP 的回答之外,我认为这也可能与 HTTP 代理机制有关。 WebSocket 连接也可以通过(HTTP)代理服务器运行。 在这种情况下,TCP keepalive 只会检查连接到代理的部分,而不会检查端到端连接。

1
我在接受这个答案和@vtortola的答案之间犹豫不决,它们都很好,但你的回答有一个更多的赞,所以我选择了你的 :) - Thomas
1
@Thomas 我觉得很奇怪你接受了一个基于其他更完整答案的答案。这里有几个重复这个信息的答案,也有几个更完整的答案。 - user207421
1
你的是...现在。代理的东西是我之前遗漏的主要点,而且这并不在你原来的回答中。 - Thomas
@Thomas 我在谈论几个更好的答案,而不仅仅是一个。 - user207421

23

http://www.whatwg.org/specs/web-apps/current-work/multipage/network.html#ping-and-pong-frames

.3.4 Ping和Pong帧

WebSocket协议规范定义了Ping和Pong帧,可以用于保持连接、心跳、网络状态探查、延迟测量等。目前API中并未公开这些功能。

用户代理可以按需发送Ping和非请求的Pong帧,例如试图维护本地网络NAT映射,检测失败的连接或者向用户显示延迟指标。用户代理不得使用Ping或非请求的Pong来帮助服务器;假设服务器将在适当时候提供需要的Pong。

WebSockets是专为RTC而开发的,因此在观察ping/pong功能时,我看到了一种测量延迟的方法。非请求的Pong必须返回与Ping相同的有效载荷,因此很方便发送时间戳,然后从客户端到服务器或反之计算延迟。


19

TCP keepalive无法通过Web代理传递。Websocket ping / pong将通过Web代理进行转发。TCP keepalive旨在监视TCP端点之间的连接。Web套接字端点不等于TCP端点。WebSocket连接可以在两个WebSocket端点之间使用多个TCP连接。


0
更新:虽然有HTTP/1.1在UDP上的实现,但WebSocket协议不支持在UDP上工作(它需要可靠的传输层)。
以下是原始(错误)答案:
除了所有指出TCP保活存在的有效问题(主要是无法通过代理),请记住HTTP(以及WebSocket)也被设计为可以在UDP上工作。

不,WebSocket只能在TCP上工作,正如我在问题中提到的那样。 - Thomas
HTTP/1.1 RFC 2616并没有指定传输协议,但它确实提到了"HTTP只假设可靠的传输;任何提供这种保证的协议都可以使用",因此UDP是不合适的选择。 - Thomas
@Thomas 你是正确的。我记忆可能在跟我开玩笑,因为我发誓我之前检查过,UDP也是一个选项...哈;-] - morgwai
...而且我甚至还记得当时的想法是“这就是为什么WebSocket需要终止消息,因为关闭UDP套接字实际上没有任何作用”...也许那是我认为已经成为标准的某个提案... ;-] - morgwai
https://zh.wikipedia.org/wiki/Universal_Plug_and_Play#协议 - Thomas
你听说过“可靠的UDP”吗?这是一种东西。此外,HTTP/3 RFC 9114运行在QUIC上而不是TCP,QUIC使用UDP。 - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接