半建立的 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,什么都没发生,没有抛出错误,接收最终超时。
我的问题
- 我所说的半建立连接有通用名称吗?
- 为什么
send()
在半建立的连接上成功?
- 为什么
receive()
只有在我先发送数据时才会失败?
欢迎提供任何帮助,尤其是指向详细解释的链接。
从客户端的角度来看,会话已完全建立,它发送 SYN,返回 SYN/ACK 并发送 ACK。只有在服务器端,您才处于半建立状态。 (即使它从服务器收到重复的 SYN/ACK,它也会重新确认,因为它处于已建立状态。)
此会话上的 send
工作正常,因为就客户端而言,会话已建立。发送的数据不必由远端确认才能成功(发送系统调用在数据复制到内核缓冲区时完成)但请参见下文。
我相信发送实际上是在连接上产生错误(可能是 RST),因为接收系统无法在会话中确认数据还没有建立完。我的猜测是 any 引用客户端套接字的系统调用发生在发送后加上短暂的延迟(即当 RST 有机会返回时)将导致错误。
接收本身永远不会导致错误,因为客户端不需要为接收做任何事情(我的意思是 TCP 协议);它只是在无所事事地等待。但是一旦你发送了一些数据,你就强迫了服务器端的手:它要么已经完成了会话建立(在这种情况下它可以接受数据)要么它必须发送一个重置(我在这里猜测它不能 "hold" 未完全建立的会话中未传送的数据)。
半熟人脉
半建立连接是指客户端调用 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
)。有关详细信息,请参阅
实验
我进行了几次实验(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,什么都没发生,没有抛出错误,接收最终超时。
我的问题
- 我所说的半建立连接有通用名称吗?
- 为什么
send()
在半建立的连接上成功? - 为什么
receive()
只有在我先发送数据时才会失败?
欢迎提供任何帮助,尤其是指向详细解释的链接。
从客户端的角度来看,会话已完全建立,它发送 SYN,返回 SYN/ACK 并发送 ACK。只有在服务器端,您才处于半建立状态。 (即使它从服务器收到重复的 SYN/ACK,它也会重新确认,因为它处于已建立状态。)
此会话上的
send
工作正常,因为就客户端而言,会话已建立。发送的数据不必由远端确认才能成功(发送系统调用在数据复制到内核缓冲区时完成)但请参见下文。我相信发送实际上是在连接上产生错误(可能是 RST),因为接收系统无法在会话中确认数据还没有建立完。我的猜测是 any 引用客户端套接字的系统调用发生在发送后加上短暂的延迟(即当 RST 有机会返回时)将导致错误。
接收本身永远不会导致错误,因为客户端不需要为接收做任何事情(我的意思是 TCP 协议);它只是在无所事事地等待。但是一旦你发送了一些数据,你就强迫了服务器端的手:它要么已经完成了会话建立(在这种情况下它可以接受数据)要么它必须发送一个重置(我在这里猜测它不能 "hold" 未完全建立的会话中未传送的数据)。