这种场景下,应用进程先发送一个SYN,然后socket进入TCP_SYN_SENT状态,这时收到了对端的SYN,而非SYN|ACK。处理流程与3.3节中客户端处理SYN|ACK的相似:tcp_v4_rcv->tcp_v4_do_rcv->tcp_rcv_state_process->tcp_rcv_synsent_state_process,这时由于包中没有ACK标记,处理流程会有不同:
5373 static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
5374 const struct tcphdr *th, unsigned int len)
5375 {
5376 struct inet_connection_sock *icsk = inet_csk(sk);
5377 struct tcp_sock *tp = tcp_sk(sk);
5378 struct tcp_fastopen_cookie foc = { .len = -1 };
5379 int saved_clamp = tp->rx_opt.mss_clamp;
...
5385 if (th->ack) { //包中不带ACK标记,不会走此路径
...
5508 return -1;
5509 }
...
5528 if (th->syn) {
5529 /* We see SYN without ACK. It is attempt of
5530 * simultaneous connect with crossed SYNs.
5531 * Particularly, it can be connect to self.
5532 */
5533 tcp_set_state(sk, TCP_SYN_RECV);//状态切换为TCP_SYN_RECV
5534
5535 if (tp->rx_opt.saw_tstamp) {
5536 tp->rx_opt.tstamp_ok = 1;
5537 tcp_store_ts_recent(tp);
5538 tp->tcp_header_len =
5539 sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
5540 } else {
5541 tp->tcp_header_len = sizeof(struct tcphdr);
5542 }
5543
5544 tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
5545 tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
5546
5547 /* RFC1323: The window in SYN & SYN/ACK segments is
5548 * never scaled.
5549 */
5550 tp->snd_wnd = ntohs(th->window);
5551 tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
5552 tp->max_window = tp->snd_wnd;
5553
5554 TCP_ECN_rcv_syn(tp, th);
5555
5556 tcp_mtup_init(sk);
5557 tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
5558 tcp_initialize_rcv_mss(sk);
5559
5560 tcp_send_synack(sk);//发送SYN|ACK
...
5560:这个负责发送SYN|ACK的函数tcp_send_synack有些特殊:
2615 int tcp_send_synack(struct sock *sk)
2616 {
2617 struct sk_buff *skb;
2618
2619 skb = tcp_write_queue_head(sk);
2620 if (skb == NULL || !(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN)) { //包一定携带SYN标记
2621 pr_debug("%s: wrong queue state\n", __func__);
2622 return -EFAULT;
2623 }
2624 if (!(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_ACK)) { //未携带ACK标记
2625 if (skb_cloned(skb)) {
2626 struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
2627 if (nskb == NULL)
2628 return -ENOMEM;
2629 tcp_unlink_write_queue(skb, sk);
2630 skb_header_release(nskb);
2631 __tcp_add_write_queue_head(sk, nskb);
2632 sk_wmem_free_skb(sk, skb);
2633 sk->sk_wmem_queued += nskb->truesize;
2634 sk_mem_charge(sk, nskb->truesize);
2635 skb = nskb;
2636 }
2637
2638 TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ACK; //加上ACK标记
2639 TCP_ECN_send_synack(tcp_sk(sk), skb);
2640 }
2641 TCP_SKB_CB(skb)->when = tcp_time_stamp;
2642 return tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); //发送出去
2643 }
在同时打开的情况下,发送队列中已经有一个SYN包等待确认。tcp_send_synack的基本功能是:将发送队列中的SYN包加上ACK标记位再发送。这样TCP收到SYN后发送的SYN|ACK的序列号与最开始发送的SYN包一致,从而使得收到对端的SYN|ACK包时,如果仅仅把这个包当做ACK使用,则其确认号正好可以确认掉之前发送的SYN|ACK,这样连接就可以在收到对端的SYN|ACK后得以正常建立。
2625-2635:如果队列中的SYN包是clone的副本,则这个skb与原本共享数据;为了不影响原本,必须copy一个新的skb——nskb(使用skb_copy产生的nskb与其源skb的数据完全一致,但承载数据的内存区完全独立的),加入ACK标记后再发送,而原来的skb必须释放掉。
SYN|ACK接收时会被当做ACK处理,流程与C/S模式下的server一致,不同的是查找连接时会在established表中找到socket,无需再建立新的socket。
在同时打开模式下,两端的TCP的数据交互流程都是:
1、发送SYN,TCP状态机跳转到TCP_SYN_SENT
2、收到SYN,TCP状态机跳转到TCP_SYN_RECV,发送SYN|ACK
3、收到ACK(被忽略SYN标记的SYN|ACK),TCP状态机跳转到TCP_ESTABLISHED,进入可以进行数据交互的状态。
接下来的数据交互并不受连接建立方式的影响,即无论是用C/S模式下的三次握手还是同时打开的方法建立的连接,连接建立完毕后的行为都是完全一样的。