半建立连接
半建立连接是指客户端调用 connect()
成功返回,但服务器调用 accept()
没有成功的连接.这可以通过以下方式发生:客户端调用 connect()
,导致向服务器发送 SYN
数据包。服务器进入状态 SYN-RECEIVED
并向客户端发送一个 SYN-ACK
数据包。这会导致客户端使用 ACK
进行回复,进入状态 ESTABLISHED
并从 connect()
调用返回。如果最后的 ACK
丢失(或被忽略,由于服务器上的接受队列已满,这可能是更可能发生的情况),服务器仍处于状态 SYN-RECEIVED
并且 accept()
不返回。由于与 SYN-RECEIVED
状态相关的超时,SYN-ACK
将被重新发送,允许客户端重新发送 ACK
。如果服务器最终能够处理 ACK
,它也会进入状态 ESTABLISHED
。否则它最终会重置连接(即发送一个 RST
给客户端)。
您可以通过在单个监听套接字上启动大量 连接来创建此场景(如果您不调整积压和tcp_max_syn_backlog
)。参见 this questions和 this article更多细节。
实验
我进行了几次实验(使用 this code 的变体)并观察到一些我无法解释的行为。所有实验都是使用 Erlang 的 gen_tcp
和当前的 Linux 执行的,但我强烈怀疑答案并非特定于此设置,因此我试图在此处使其更通用。
connect()
-> wait -> send()
-> receive()
我的出发点是从客户端建立连接,等待 1 到 5 秒,向服务器发送“Ping”消息并等待回复。通过此设置,我观察到 receive()
失败并出现错误 closed
当我建立了一半的连接时。在半建立的连接上 send()
期间从未出现错误。您可以找到此设置的更详细说明 here .
connect()
-> 漫长的等待 -> send()
为了查看,如果我在半建立的连接上发送数据时出现错误,我在发送数据前等待了 4 分钟。 4 分钟应涵盖与半建立连接相关的所有超时和重试。仍然可以发送数据,即 send()
无错误返回。
connect()
-> receive()
接下来,我测试了如果我只调用 receive()
并且超时时间很长(5 分钟)会发生什么。我的期望是为半建立的连接得到一个 closed
错误,就像在最初的实验中一样。唉,什么也没发生,没有抛出任何错误,接收最终超时。
我的问题
- 我称之为半建立的连接是否有一个通用名称?
- 为什么
send()
在半建立的连接上成功? - 为什么
receive()
只有在我先发送数据时才会失败?
欢迎提供任何帮助,尤其是指向详细解释的链接。
最佳答案
从客户端的角度来看, session 已完全建立,它发送 SYN,返回 SYN/ACK 并发送 ACK。只有在服务器端,您才处于半建立状态。 (即使它从服务器收到重复的 SYN/ACK,它也只会重新确认,因为它处于已建立状态。)
此 session 中的
send
工作正常,因为就客户端而言, session 已建立。发送的数据不必由远端确认才能成功(发送系统调用在数据复制到内核缓冲区时完成),但请参见下文。我相信发送实际上是在连接上生成错误(可能是 RST),因为接收系统无法在尚未完成建立的 session 上确认数据。我的猜测是 任何 引用客户端套接字的系统调用发生在发送后加上一个短暂的延迟(即当 RST 有机会返回时)将导致错误。
接收本身永远不会导致错误,因为客户端不需要为接收做任何事情(我的意思是 TCP 协议(protocol));它只是在无所事事地等待。但是一旦你发送了一些数据,你就强制了服务器端的手:它要么已经完成了 session 建立(在这种情况下它可以接受数据)要么它必须发送一个重置(我在这里猜测它不能“保持” "未完全建立的 session 中未传送的数据)。
关于sockets - 半建立的 TCP 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37997708/