半建立的TCP连接

4

半建立连接

我所谓的半建立连接是指客户端调用connect()成功返回,但服务器调用accept()失败。这可能会发生以下方式:客户端调用connect(),导致向服务器发送一个SYN数据包。服务器进入状态SYN-RECEIVED并向客户端发送一个SYN-ACK数据包。这将导致客户端回复ACK,进入状态ESTABLISHED并从connect()调用中返回。如果最终的ACK丢失(或由于服务器上的完整接受队列而被忽略,这可能是更可能的情况),则服务器仍处于状态SYN-RECEIVEDaccept()不会返回。由于与SYN-RECEIVED状态相关联的超时,SYN-ACK将被重新发送,允许客户端重新发送ACK。如果服务器能够最终处理ACK,它也将进入状态ESTABLISHED。否则,它将最终重置连接(即向客户端发送一个RST)。
你可以通过在单个侦听套接字上启动大量连接(如果您不调整backlog和tcp_max_syn_backlog)来创建此场景。有关更多详细信息,请参见this questionsthis article

实验

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

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

我的起点是建立一个从客户端到服务器的连接,等待1到5秒钟后发送一个“Ping”消息到服务器并等待回复。在这种设置下,当我有一个半建立的连接时,我观察到receive()失败并出现错误closed。在半建立的连接上,send()从未出现过错误。您可以在此处找到更详细的描述。 connect() -> 长时间等待 -> send() 为了查看是否可以在半建立的连接上发送数据而出现错误,我等待了4分钟才发送数据。4分钟应该涵盖与半建立连接相关的所有超时和重试。发送数据仍然是可能的,即send()返回而没有错误。 connect() -> receive()

接下来我测试了只使用非常长的超时时间(5分钟)调用receive()会发生什么。我的期望是对于半建立连接会收到一个closed错误,就像在原始实验中一样。但遗憾的是,没有任何反应,也没有抛出错误,最终receive()超时。

我的问题

  1. 半建立连接是否有通用名称?
  2. 为什么在半建立连接上执行send()操作成功?
  3. 为什么只有在发送数据后才执行receive()操作会失败?

任何帮助,特别是详细解释的链接,都将受到欢迎。

1个回答

5
  1. 从客户端的角度来看,会话已经完全建立,它发送了SYN,收到了SYN/ACK并发送了ACK。只有在服务器端才存在半连接状态。(即使它从服务器获得了重复的SYN/ACK,它也只会重新发送ACK,因为它已经处于建立状态。)

  2. 这个会话上的send函数可以正常工作,因为对于客户端来说,会话已经建立。发送的数据不必被远程端确认以便成功(当数据被复制到内核缓冲区时,发送系统调用即结束),但见下面的内容。

  3. 我认为这里发送实际上在连接上生成了一个错误(可能是RST),因为接收系统不能在没有完成连接的情况下ACK数据。我猜测,在发送之后一段时间(即RST有机会返回时),任何引用客户端套接字的系统调用都会导致错误。

    仅仅接收数据不会导致错误,因为客户端不需要进行任何事情(我指的是TCP协议方面)来接收数据;它只是闲置等待。但是一旦您发送了一些数据,您就迫使了服务端:它要么已经完成了会话建立(在这种情况下,它可以接受数据),要么必须发送重置(我猜测它不能在没有完全建立的会话上“持有”未传递的数据)。


关于第2点和第3点:如果出现像connect() -> 长时间等待 -> send() -> 等待 -> send()这样的情况,您会期望看到错误吗? - jvf
是的,我认为是这样的。确切的行为可能取决于运行服务器端的操作系统。 - Gil Hamilton
确实,connect() -> 等待 -> send() -> 短暂等待 -> 第二个send()失败了! - jvf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接