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 请求/响应对。

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

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

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

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

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

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

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

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

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

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

重置问题是TCP独有的还是可能的未知 也可以在其他传输连接协议中找到。

解决方法

但是为什么客户端会在收到带有 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,其套接字将被标记为已损坏。在使用此套接字的下一次系统调用(即通常为 readwrite)时,此错误将传递给应用程序 — 无论套接字的接收缓冲区中是否仍有未读数据.这些未读数据因此丢失。

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

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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...