首页 > 解决方案 > HTTP/1.1 如何解决 TCP 重置问题?

问题描述

我试图理解RFC 7230: HTTP/1.1 Message Syntax and Routing , § 6.6中提到的TCP 重置问题

6.6. 拆除

Connection 头域(第 6.1 节)提供了一个“关闭”连接选项,当发送者希望在当前请求/响应对之后关闭连接时,应该发送该选项。

所以 HTTP/1.1 具有持久连接,这意味着可以在同一个连接上发送多个 HTTP 请求/响应对。

发送“关闭”连接选项的客户端不得在该连接上发送进一步的请求(在包含“关闭”的连接之后),并且必须在读取与该请求对应的最终响应消息后关闭连接。

接收“关闭”连接选项的服务器必须在向包含“关闭”的请求发送最终响应后启动连接的关闭(见下文)。服务器应该在它对该连接的最终响应中发送一个“关闭”连接选项。服务器不得处理在该连接上收到的任何进一步请求。

因此,客户端通过在最后一个 HTTP请求中添加Connection: close标头字段来表示它将关闭连接,并且只有在它收到确认服务器收到请求的 HTTP 响应后才会关闭连接。

发送“关闭”连接选项的服务器必须在发送包含“关闭”的响应后启动连接的关闭(见下文)。服务器不得处理在该连接上收到的任何进一步请求。

接收到“关闭”连接选项的客户端必须停止在该连接上发送请求,并在读取包含“关闭”的响应消息后关闭连接;如果在连接上发送了额外的流水线请求,客户端不应该假设它们将由服务器处理。

因此,服务器通过在最后一个 HTTP响应中添加Connection: close标头字段来表示它将关闭连接,然后关闭连接。但是它只有在收到确认客户端收到 HTTP 响应的消息后才关闭连接?

如果服务器立即关闭 TCP 连接,则客户端将无法读取最后的 HTTP 响应的风险很大。如果服务器在完全关闭的连接上收到来自客户端的额外数据,例如客户端在收到服务器响应之前发送的另一个请求,则服务器的 TCP 堆栈将向客户端发送重置数据包;不幸的是,重置数据包可能会在客户端的 HTTP 解析器读取和解释之前删除客户端未确认的输入缓冲区。

因此,在服务器发起关闭连接的情况下,如果服务器在向初始 HTTP 请求发送带有标头字段的 HTTP 响应Connection: close立即完全关闭连接,那么客户端可能不会收到该 HTTP 响应,因为它收到了一个TCP 将数据包响应重置为在初始 HTTP 请求之后发送的后续 HTTP 请求。但是,TCP 重置数据包对后续HTTP 请求的响应如何于对初始HTTP 请求的 HTTP 响应呢?

为了避免 TCP 重置问题,服务器通常分阶段关闭连接。首先,服务器通过仅关闭读/写连接的写入端来执行半关闭。然后服务器继续从连接中读取,直到它收到客户端相应的关闭,或者直到服务器合理地确定它自己的 TCP 堆栈已收到客户端对包含服务器最后响应的数据包的确认。最后,服务器完全关闭连接。

因此,在服务器发起关闭连接的情况下,服务器仅在向初始 HTTP 请求发送带有标头字段的 HTTP 响应后立即关闭连接的写入端,并且仅关闭连接的读取端在接收到带有标头字段的后续相应 HTTP 请求之后,或者在等待足够长的时间以假定它接收到确认客户端收到 HTTP 响应的 TCP 消息之后。但是为什么客户端在收到HTTP响应后会发送一个带有头字段的后续相应HTTP请求?Connection: closeConnection: closeConnection: closeConnection: close头字段,而第 5 段指出:'接收“关闭”连接选项的客户端必须停止在该连接上发送请求'?

不知道重置问题是 TCP 独有的还是也可能在其他传输连接协议中发现。

标签: socketshttptcpnetwork-programmingpersistent-connection

解决方案


但是为什么客户端会在收到带有 Connection: close 头字段的 HTTP 响应后发送带有 Connection: close 头字段的后续相应 HTTP 请求,而第 5 段指出:'接收“关闭”连接选项的客户端必须停止发送对该连接的请求'?

使用 HTTP 流水线,客户端可以发送新请求,即使之前请求的响应(以及Connection: close此响应中的响应)尚未收到。这是在收到前一个请求的响应后才发送下一个请求的轻微优化,但它带来了服务器不会处理这个新请求的风险。

但是,TCP 重置数据包对后续 HTTP 请求的响应如何先于对初始 HTTP 请求的 HTTP 响应呢?

虽然 TCP RST 将在响应之后发送,但它会提前传播到应用程序。如果新数据到达已经关闭以至少读取(即close(fd)shutdown(fd, SHUT_RD))的套接字,则发送 TCP RST。如果在关闭时套接字的接收缓冲区中仍有未处理的数据,它也会被发送,例如在 HTTP 流水线的情况下。一旦对等方接收到 TCP RST,其套接字将被标记为损坏。在使用此套接字的下一次系统调用(即通常为 areadwrite)时,此错误将被传递给应用程序——无论套接字的接收缓冲区中是否仍有未读数据。这些未读数据因此丢失。

但是它只有在收到确认客户端收到 HTTP 响应的消息后才关闭连接?

它不等待来自客户端的某些应用程序消息。它将首先使用 传递响应Connection: close,然后在套接字上读取以确定客户端是否关闭了连接。然后它也会关闭连接。这种等待关闭当然应该在很短的超时时间内完成,因为中断的连接可能会导致连接永远不会被显式关闭。或者,它可以等待几秒钟,并希望客户端同时获得并处理响应。


推荐阅读