半建立的 TCP 连接
Posted
技术标签:
【中文标题】半建立的 TCP 连接【英文标题】:Half-Established TCP Connections 【发布时间】:2016-10-26 03:31:59 【问题描述】:半建立连接
对于半建立的连接,我的意思是客户端对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()
-> 等待 -> send()
-> receive()
我的出发点是从客户端建立连接,等待 1 到 5 秒,向服务器发送“Ping”消息并等待回复。通过这个设置,我观察到receive()
失败并出现错误closed
当我有一个半建立的连接时。在半建立的连接上send()
期间从未出现错误。您可以在here 找到有关此设置的更详细说明。
connect()
-> 漫长的等待 -> send()
为了查看,如果我在半建立的连接上发送数据时出现错误,我在发送数据前等待了 4 分钟。这 4 分钟应涵盖与半建立连接相关的所有超时和重试。仍然可以发送数据,即 send()
返回没有错误。
connect()
-> receive()
接下来我测试了如果我只用很长的超时时间(5 分钟)调用receive()
会发生什么。我的期望是得到一个closed
错误的半建立连接,就像在原始实验中一样。唉,什么都没有发生,没有抛出任何错误,最终接收超时。
我的问题
-
我所说的半建立连接是否有一个通用名称?
为什么在半建立的连接上
send()
会成功?
为什么 receive()
只有在我先发送数据时才会失败?
欢迎提供任何帮助,尤其是详细说明的链接。
【问题讨论】:
【参考方案1】:从客户端的角度来看,会话已经完全建立,它发送 SYN,返回 SYN/ACK 并发送 ACK。只有在服务器端,您才处于半建立状态。 (即使它从服务器收到重复的 SYN/ACK,它也会重新 ACK,因为它处于建立状态。)
此会话上的send
工作正常,因为就客户端而言,会话已建立。发送的数据不必由远端确认才能成功(当数据复制到内核缓冲区时,发送系统调用完成),但请参见下文。
我相信这里发送实际上正在在连接上产生错误(可能是 RST),因为接收系统无法在它尚未完成建立的会话上确认数据。我的猜测是,在发送后发生的引用客户端套接字的 任何 系统调用加上短暂的延迟(即当 RST 有机会返回时)将导致错误。
接收本身永远不会导致错误,因为客户端不需要为接收做任何事情(我的意思是 TCP 协议);它只是在等待。但是一旦你发送了一些数据,你就强迫了服务器端:它要么完成了会话建立(在这种情况下它可以接受数据),要么它必须发送一个重置(我猜这里它不能“保持” " 未完全建立的会话中未传递的数据)。
【讨论】:
关于 2. 和 3.:您可能会看到类似connect()
-> long wait -> send()
-> wait -> send()
这样的错误?
是的。我会这么认为。确切的行为可能确实取决于运行服务器端的操作系统。
确实,connect()
-> 等待 -> send()
-> 短暂等待 -> send()
在第二个 send()
上失败!以上是关于半建立的 TCP 连接的主要内容,如果未能解决你的问题,请参考以下文章