半建立的 TCP 连接

Half-Established TCP Connections

半熟人脉

半建立连接是指客户端调用 connect() return 成功,但服务器调用 accept() 失败的连接。这可以通过以下方式发生:客户端调用 connect(),导致向服务器发送 SYN 数据包。服务器进入状态 SYN-RECEIVED 并向客户端发送一个 SYN-ACK 数据包。这会导致客户端回复 ACK,进入状态 ESTABLISHED 并从 connect() 调用进入 return。如果最后的 ACK 丢失(或被忽略,由于服务器上的接受队列已满,这可能是更可能发生的情况),服务器仍处于状态 SYN-RECEIVED 并且 accept() 没有 return。由于与 SYN-RECEIVED 状态相关的超时,SYN-ACK 将被重新发送,允许客户端重新发送 ACK。如果服务器最终能够处理 ACK,它也会进入状态 ESTABLISHED。否则它最终会重置连接(即发送 RST 到客户端)。

您可以通过在单个侦听套接字上启动 lots 连接来创建此方案(如果您不调整积压和 tcp_max_syn_backlog)。有关详细信息,请参阅 and this article

实验

我进行了几次实验(this code 的变体)并观察到一些我无法解释的行为。所有实验都是使用 Erlang 的 gen_tcp 和当前的 Linux 执行的,但我强烈怀疑答案并非特定于此设置,因此我试图在此处使其更通用。

connect() -> 等待 -> send() -> receive()

我的出发点是从客户端建立连接,等待 1 到 5 秒,向服务器发送 "Ping" 消息并等待回复。通过此设置,我观察到 receive() 失败并出现错误 closed 当我建立了一半的连接时。在半建立连接的 send() 期间从未出现过错误。您可以找到此设置的更详细说明 here

connect() -> 漫长的等待 -> send()

为了查看,如果我在半建立的连接上发送数据时出现错误,我在发送数据前等待了 4 分钟。 4 分钟应涵盖与半建立连接相关的所有超时和重试。发送数据仍然是可能的,即 send() returned 没有错误。

connect() -> receive()

接下来我测试了如果我只调用 receive() 并且超时时间很长(5 分钟)会发生什么。我的期望是在建立一半的连接上出现 closed 错误,就像在最初的实验中一样。 las,什么都没发生,没有抛出错误,接收最终超时。

我的问题

  1. 我所说的半建立连接有通用名称吗?
  2. 为什么send()在半建立的连接上成功?
  3. 为什么 receive() 只有在我先发送数据时才会失败?

欢迎提供任何帮助,尤其是指向详细解释的链接。

  1. 从客户端的角度来看,会话已完全建立,它发送 SYN,返回 SYN/ACK 并发送 ACK。只有在服务器端,您才处于半建立状态。 (即使它从服务器收到重复的 SYN/ACK,它也会重新确认,因为它处于已建立状态。)

  2. 此会话上的 send 工作正常,因为就客户端而言,会话已建立。发送的数据不必由远端确认才能成功(发送系统调用在数据复制到内核缓冲区时完成)但请参见下文。

  3. 我相信发送实际上在连接上产生错误(可能是 RST),因为接收系统无法在会话中确认数据还没有建立完。我的猜测是 any 引用客户端套接字的系统调用发生在发送后加上短暂的延迟(即当 RST 有机会返回时)将导致错误。

    接收本身永远不会导致错误,因为客户端不需要为接收做任何事情(我的意思是 TCP 协议);它只是在无所事事地等待。但是一旦你发送了一些数据,你就强迫了服务器端的手:它要么已经完成了会话建立(在这种情况下它可以接受数据)要么它必须发送一个重置(我在这里猜测它不能 "hold" 未完全建立的会话中未传送的数据)。