解释HTTP Keep-Alive机制

61
保持连接是为了在每个新请求中减少创建和关闭套接字连接的显着开销,因此将Keep-alives添加到HTTP中。以下是它在HTTP 1.0和1.1中的工作原理摘要:
HTTP 1.0规范并没有详细探讨Keep-Alive应该如何工作。基本上,支持Keep-Alive的浏览器会向请求附加一个额外的标头,如下所述:
当服务器处理请求并生成响应时,它还会向响应添加一个标头:
Connection:Keep-Alive 这样做后,套接字连接不会像以前那样关闭,而是在发送响应后保持打开状态。当客户端发送另一个请求时,它将重用相同的连接。连接将继续重用,直到客户端或服务器决定对话已经结束,并且其中一个断开连接。
以上解释来自这里。但我不明白一件事。
当这样做时,套接字连接不会像以前那样被关闭,而是在发送响应后保持打开状态。 我理解我们只是通过发送TCP数据包来进行请求和响应,这个“套接字连接”如何发挥作用并工作呢?我们仍然需要发送数据包,但它又如何建立持久连接呢?这似乎不真实。

1
@JakeGould:感谢你的编辑。我认为这是合适的,类似的问题以前也被问过。例如:https://dev59.com/e3M_5IYBdhLWcg3wNgKZ,但是他们并没有解释这个“套接字连接”实际上是如何工作的。 - good_evening
@晚上好 那是一个老问题,当时这样的问题还可以。正如JakeGould建议的那样,您可以尝试Server Fault或[networkengineering.se]。 - Bleeding Fingers
7
我认为这篇内容比在服务器错误网站上更合适,因为它只是要求解释可以帮助程序员的技术。 - Igor Čordaš
2
@晚上好 对我来说听起来很容易理解。HTTP模型就像给你的朋友打电话一样。你打电话问问题,他回答,然后你挂断电话。你为每个问题重复这个过程。保持连接意味着你不挂断电话,只要你们有话题就可以随时交流。我想另一个例子是,当你登录一个网站并保存了你的凭据,这样你下次访问它时就不必再次登录。 - The Muffin Man
5个回答

94

建立新的TCP连接时会有额外开销,包括DNS查找、TCP握手、SSL/TLS握手等。如果不使用保持连接(keep-alive),每个HTTP请求都必须建立新的TCP连接,然后在响应被发送/接收后关闭该连接。保持连接允许复用现有的TCP连接进行多个请求/响应,从而避免所有这些开销。这就是使连接“持久”的原因。

在HTTP 0.9和1.0中,默认情况下,服务器在向客户端发送响应后关闭其TCP连接的一端。客户端必须在接收到响应后关闭其TCP连接的一端。在HTTP 1.0中(但不是在0.9中),客户端可以通过在请求中包含Connection: keep-alive头来显式要求服务器不要关闭其连接一侧。如果服务器同意,则在响应中包含Connection: keep-alive头并且不关闭其连接一侧。然后客户端可以复用相同的TCP连接发送其下一个请求。

在HTTP 1.1中,除非客户端通过在请求中包含Connection: close头,或者服务器决定在其响应中包含Connection: close头来要求关闭连接,否则keep-alive是默认行为。


2
谢谢。这就是我没有完全理解的地方,为什么TCP连接很难创建。除了DNS查找、TCP握手、SSL/TLS握手之外,还需要做什么? - good_evening
18
DNS、TCP 和 SSL 并非轻量级系统,它们每个都需要时间和资源才能执行它们各自的步骤,然后才能进行下一个步骤。在建立 TCP 连接之前,DNS 必须将主机名解析为 IP 地址。在建立 SSL 会话之前,TCP 必须执行三次握手以建立新连接。SSL 需要多次握手来回交换加密信息。在给定的客户端/服务器对之间执行这些步骤的次数越少,HTTP 请求的发送和响应速度就越快。 - Remy Lebeau
从答案中我得到的是,如果标志位存在于头部,连接将保持活动状态。我不明白最终是谁决定关闭连接。我不明白服务器如何向客户端发送响应并在不关闭连接和强制EOF的情况下信号结束响应,而这通常会在connection:close情况下信号响应的结束。从这个答案中我只知道HTTP 1.1默认为tcp keep alive,之前的版本需要指定。我没有得到关于如何应用于HTTP keep-alive的答案。 - user562566
@RemyLebeau 我并不是故意刁难。我在编写代理时遇到了这个问题,因为即使我在客户端和服务器之间透明地传递数据,即使当我到达响应的末尾并将其发送回客户端时,客户端仍然会保持连接打开状态而不请求任何其他内容。所有数据都成功传输。由于它是tcp-keep alive,我从未收到也从未发送EOF。如果我强制关闭,客户端最终会做出反应,因为EOF已经到达。所以我一定漏掉了什么,而这个答案没有提供所需的机制方面的见解。 - user562566
2
@TechnikEmpire:请阅读HTTP规范,特别是RFC 2616第4.4节。有几种方法可以在不关闭连接的情况下标识HTTP消息的结束。客户端必须分析服务器的响应头来确定实际使用的机制。如果您正在编写自己的代理程序,请务必小心,不要搞砸它们。您不能盲目地发送所有内容,您可能需要分析/调整HTTP消息,因为保持活动状态是基于每个连接处理的,而代理有2个连接。 - Remy Lebeau
@RemyLebeau,感谢提供的链接,我已经了解了RFC中概述的内容。我认为我面临的问题可能源于不正确地处理保持活动状态的管线事务。今晚我将重写我的解析方法,我相信这将解决问题。 - user562566

28

让我们来打个比方。HTTP 的作用是发送一个请求并获取响应。这就类似于向某人提问并获得回答。

问题在于问题和答案需要通过网络传输。为了通过网络进行通信,使用 TCP(套接字)。这就类似于使用电话向某人提问并得到回答。

当您加载包含 2 张图片的网页时,HTTP 1.0 包括以下步骤:

  • 打电话
  • 请求网页
  • 获取网页
  • 结束通话
  • 打电话
  • 请求第一张图片
  • 获取第一张图片
  • 结束通话
  • 打电话
  • 请求第二张图片
  • 获取第二张图片
  • 结束通话

打电话和结束通话需要时间和资源。控制数据(如电话号码)必须通过网络传输。更有效的方法是打一个电话获取页面和两张图片。这就是保持活动连接所允许的。有了保持活动连接,上述过程将变成:

  • 打电话
  • 请求网页
  • 获取网页
  • 请求第一张图片
  • 获取第一张图片
  • 请求第二张图片
  • 获取第二张图片
  • 结束通话

1
谢谢,是的,我理解了。但是我们如何才能让它只使用“一个电话呼叫”就能实现?在第一阶段(“打电话”)客户端和服务器之间会发生什么? - good_evening
第一阶段:http://en.wikipedia.org/wiki/Transmission_Control_Protocol#Connection_establishment。之后发生的事情由HTTP协议决定。服务器期望收到一个HTTP请求并用HTTP响应进行回答。然后它期望另一个请求,以此类推。 - JB Nizet

22

这确实是一个与网络有关的问题,但毕竟在这里可能是合适的。

混淆的原因在于数据包导向和流导向连接之间的区别。

互联网通常被称为“TCP/IP”网络。在低层次(IP,互联网协议)上,互联网是数据包导向的。主机将数据包发送到其他主机。

然而,在IP之上,我们有TCP(传输控制协议)。互联网的这一层的整个目的是隐藏底层介质的数据包导向性质,并将两个主机之间的连接(主机和端口,更准确地说)呈现为数据流,类似于文件或管道。然后,我们可以在操作系统API中打开一个套接字来表示该连接,并且我们可以将该套接字视为文件描述符(在Unix中实际上是FD,在Windows中非常类似于文件句柄)。

大多数其他的互联网客户端-服务器协议(HTTP、Telnet、SSH、SMTP)都是建立在TCP之上的。因此,客户端打开一个连接(套接字),将其请求写入套接字(作为底层IP中的一个或多个数据包传输),从套接字读取响应(响应也可以包含来自多个IP数据包的数据),然后...然后选择保持连接以进行下一次请求或关闭它。预先保持活动状态的HTTP总是关闭连接。新的客户端和服务器可以保持连接。

保持活动状态的优点在于建立连接是昂贵的。对于短请求和响应,可能需要比实际数据交换更多的数据包。

轻微的缺点可能是服务器现在必须告诉客户端响应的结束位置。服务器不能简单地发送响应并关闭连接。它必须告诉客户端:“读取20KB,这将是我的响应的结尾”。因此,服务器必须事先知道响应的大小,并作为更高级别协议的一部分(例如HTTP中的Content-Length:)通知客户端。或者,服务器可以发送一个分隔符来指定响应的结尾 - 这完全取决于TCP上面的协议。


3
HTTP有多种方式来终止响应,这取决于响应的格式(普通、分块或MIME)。并非总是使用/可行的Content-Length - Remy Lebeau
2
我真的很喜欢这个答案,虽然它不是完全符合问题,但你在回答中写的正是我所需要的。并且为了让这不仅仅是一个感谢评论,是的,Content-Length并不总是适用的,但有其他的方法告诉客户端响应结束的位置,这才是最重要的,因为保持连接开放将失去“读取所有然后结束”的可能性。 - Igor Čordaš

3
您可以这样理解:

HTTP使用TCP作为传输协议。在通过TCP发送和接收数据包之前,

  1. 客户端需要发送连接请求
  2. 服务器响应
  3. 数据传输完成
  4. 连接关闭。

然而,如果我们使用keep-alive功能,则在接收数据后连接不会关闭。连接保持活动状态。

这有助于提高性能,因为对于下一次调用,连接建立将不会发生,因为与服务器的连接已经存在。这意味着所需时间更少。虽然连接所需时间很短,但在每毫秒都至关重要的系统中,这确实会产生很大的影响。


0

保持连接体现了泄漏抽象法则。虽然HTTP有意设计为无状态协议,但它建立在本质上是有状态的TCP之上。因此,我们必须做出某些妥协以防止性能下降。


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