simultaneous connect: 同时open tcp

 
tcp_fast_path_on(tp);
if (sk->sk_shutdown & SEND_SHUTDOWN)
  tcp_shutdown(sk, SEND_SHUTDOWN);
if (sk->sk_socket)<-- 删除这部分代码
  goto consume; <-- 删除这部分代码
break;

case TCP_FIN_WAIT1: {

image

去掉这几行代码后会调用tcp_data_queue 发送ack等数据

普通 connect:
CLOSED → SYN_SENT → ESTABLISHED

服务端 accept:
LISTEN → SYN_RECV → ESTABLISHED

simultaneous connect:
CLOSED → SYN_SENT
            ↓ 收到 SYN
         SYN_RECV
            ↓ 收到 SYN+ACK
         ESTABLISHED

 

 

我们把两个主机(A 和 B)同时发起连接的过程拆解一下,你就明白这个状态是怎么来的了:

  1. 初始状态:A 和 B 都是 CLOSED

  2. 同时发起

    • A 调用 connect(),发送 SYN (Seq=100)。A 进入 SYN_SENT 状态。

    • B 调用 connect(),发送 SYN (Seq=200)。B 进入 SYN_SENT 状态。

  3. SYN 包在网络中“擦肩而过”

  4. 收到对方的 SYN(关键点来了!):

    • A 在处于 SYN_SENT 状态时,收到了 B 发来的 SYN (Seq=200)。

    • 根据 TCP 协议(RFC 793),A 意识到:“咦,我也想连你,你正好也想连我?”

    • A 必须对 B 的 SYN 进行确认。于是 A 发送 SYN+ACK (Seq=100, Ack=201)。

    • 此时,A 的状态从 SYN_SENT 变为 SYN_RCVD (即 Linux 中的 TCP_SYN_RECV)。

  5. 建立连接

    • B 也会经历同样的过程,变成 SYN_RCVD 并发送 SYN+ACK。

    • 当 A 收到 B 的 SYN+ACK 时,A 回复 ACK,状态变为 ESTABLISHED

但是tcp sock变为ESTABLISHED 后;在 TCP 状态从 TCP_SYN_RECV 切换到 TCP_ESTABLISHED 时,即使是同时连接的情况,发送一个 ACK(会被视为 DUPACK 或 DSACK)也是可以接受的,甚至是预期的行为。因此,之前的“优化”是不必要的,且弊大于利。

如果不发送ack;此时会出现:

TCP Fast Open 允许在三次握手的 ACK 包中携带数据。

  • 问题:如果第3个 ACK 包含数据,且连接在收到数据前已经被 accept(),之前添加的 goto consume 逻辑会导致这些数据被忽略。

  • 后果:数据没有被处理,也就不会被确认(ACKed),导致 TFO 功能异常。

Simultaneous Connect),为什么要检查 if (sk->sk_socket)

它的真实意图是区分“主动打开(Active Open)”和“被动打开(Passive Open)”。

  • 主动方 (Client):

    • 是你主动调用的 socket()connect()

    • 所以在握手开始前,你的 struct sock 就已经和 struct socket 绑好了。

    • 特征sk->sk_socket 不为空

  • 被动方 (Server):

    • 服务器收到 SYN 包时,内核会在底层悄悄创建一个新的 struct sock(子 socket)来处理这个连接请求。

    • 在三次握手完成并被用户调用 accept() 取走之前,这个临时的子 socket 往往还没有完全分配好对应的上层 struct socket(或者说还没跟文件描述符绑定)。

    • 特征:在握手早期的某些阶段,其 sk->sk_socket 可能是 NULL 或者处于特殊状态。

 

posted @ 2025-12-15 21:45  codestacklinuxer  阅读(0)  评论(0)    收藏  举报