说客户端有多个文件要通过 HTTP Post 方法发布到服务器,将会有两个 api 调用。因为我不想创建两个 TCP 连接,而且我想重用 TCP 连接,所以我对第一个请求使用了 Http keep-alive 标头,现在我只需要在第一个请求时建立一个 TCP 连接来发送多个文件
但是服务器如何区分这两个文件呢?当我们不使用keep alive时,客户端的操作系统会发送EOF标志(文件结束)来指示服务器文件已完成传输
以下是我的假设,不确定是否正确:
现在我们使用 keep-alive,因此服务器上的 EOF 不会关闭连接,因此可以通过同一连接发送第二个文件并以 EOF 结束。但是当客户端不想再发送任何文件时,它如何关闭 TCP 连接?如果客户端发送另一个 EOF,服务器可能会认为他将发送另一个文件?
我唯一能想到的是,假设您只有 5 个文件要发送,在第 5 个 HTTP 请求中,您不会发送 keep-alive 标头,但第 1、2、3 和 4 个请求都需要发送 keep-alive 标头,我的理解正确吗?
答案1
当我们不使用保持活动时,客户端的操作系统会发送 EOF 标志(文件结束)来指示服务器文件已完成传输
不是。HTTP 请求的结束可以通过接收分隔空行(对于没有正文的请求)或读取请求的“Content-Length”标头中指定的确切字节数(对于具有固定长度正文的请求)或读取 0 字节块(对于分块请求)来检测。
其他答案链接的 RFC 9112 详细讨论了消息框架,特别是第 6.3 节“消息正文长度”。
现在我们使用 keep-alive,因此服务器上的 EOF 不会关闭连接,因此可以通过同一连接发送第二个文件并以 EOF 结束
这不对。除了关闭发送方的连接(shutdown(SHUT_WR) 操作)之外,TCP 没有其他“发送 EOF 标志”的方法,这是最终决定 — 无法再向该方向发送任何数据,并且通常也会导致另一个方向的关闭。
更一般地说,“EOF”不是一个实际的消息;在编程语言中,它由缺席消息的边界 – 从 read() 操作获得 0 字节结果后,将引发 EOF。但 TCP 无法随意发出此信号,因为它不会向接收方报告消息边界;它实际上无法传递 0 字节消息。(事实上,空 TCP 段通常用作“保持活动”机制,特别是因为它们不导致接收器看到 0 字节读数。)
(换句话说:您不能通过发送 EOF 信号来关闭 TCP 连接,而应该通过关闭 TCP 连接来发送 EOF 信号。)
这就是为什么 RFC 9112 使用术语“关闭”,例如“请求消息永远不会以关闭的方式分隔[…]”而不是“EOF 分隔”。
答案2
Connection: keep-alive
HTTP/1.1 隐含该标头,即仅 HTTP/1.0 才需要该标头。在持久性 HTTP/1 中,一个请求会在另一个请求之后发送,响应会以相同顺序返回。HTTP/1 还明确定义了消息(请求、响应)的结束位置(Content-Length、Transfer-Encoding 标头),因此正确实施 HTTP/1 的应用程序可以检测到消息边界。
具体技术细节请参见标准
答案3
如果客户端发送了 EOF,则不能通过同一连接发送另一个文件。与文件和终端不同,TCP 中的 EOF 是该连接的永久条件。它通过发送包含标志的段来表示FIN
,除了确认从另一端发送的段之外,不能发送其他数据。
当使用保持活动时,将使用不同的方法来指示所发送数据的结尾。如果在传输之前知道数据的大小,Content-Length
通常会发送标头,接收方只需读取那么多字节即可。如果数据是动态生成的,则Transfer-Encoding: chunked
使用。这会将数据作为块序列发送<data-size><data-bytes>
,并通过发送大小为 0 的块来表示结尾。分块编码最常用于由脚本生成的服务器动态回复,而不是上传和下载文件,因为可以在发送文件之前确定文件大小。