Linux tcp連接狀態之SYNC_RECV

1、按tcp/ip協議的描述,tcp三次握手過程,tcp的狀態遷移如下所示:

1)、客戶端通過connect系統調用向處於LISTEN狀態的服務端發送sync請求,客戶端進入SYNC_SEND狀態;

2)、服務端收到sync報文後,向客戶端發送sync+ack報文,服務端進入SYNC_RECV狀態;

3)、客戶端收到sync+ack後,進入ESTABLISHED狀態,並向服務端發送ack,服務端接收到ack後,進入ESTABLISHED狀態。

     (sys_connect)          sync
   client(SYNC_SEND)   ---------->    server(LISTEN)
                           sync+ack
   client(SYNC_SEND)   <----------    server(SYNC_RECV)
                            ack
   client(ESTABLISHED) ---------->    server(ESTABLISHED)

2、但Linux的實現跟這裏的描述並非完全一致,在Linux裏,服務端收到sync報文並向客戶端發送sync+ack後,並沒有將自己置位SYNC_RECV狀態,而是申請了一個request_sock,並將request_sock掛到sock的半連接隊列裏,等收到客戶端的ack時,再從半連接隊列裏找到request_sock,並將其置位ESTABLISHED,然後掛到全連接隊列中。

3、相關實現:

client端:

1)、client端調用connect系統調用發送sync報文,最終調用tcp_v4_connect,在tcp_v4_connect裏通過tcp_set_state(sk, TCP_SYN_SENT)將sk狀態置位SYNC_SNET;
2)、client收到server的sync+ack後,將狀態置爲ESTABLISHED。
tcp_v4_rcv
    tcp_v4_do_rcv
        tcp_rcv_state_process
            tcp_rcv_synsent_state_process
                tcp_finish_connect
                    tcp_set_state(sk, TCP_ESTABLISHED)

server端:

tcp_v4_rcv
	tcp_v4_do_rcv
		int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
		{
			//收到sync包時,狀態爲TCP_LISTEN,條件成立
			if (sk->sk_state == TCP_LISTEN) {
				//這裏是從半連接隊列的syn_table裏找request_sock,
				//1、如果是sync報文,這裏返回爲空,跳轉到tcp_rcv_state_process
				//在函數tcp_rcv_state_process裏會創建一個新的request_sock,然後掛到半連接隊列裏
				//具體流程:tcp_rcv_state_process->tcp_v4_conn_request->tcp_conn_request->inet_csk_reqsk_queue_hash_add
				//2、如果是client的ack報文,則這裏返回的nsk爲第1步創建的request_sock,並且在tcp_v4_hnd_req->tcp_check_req裏
				//將sk->sk_state狀態置位SYNC_RECV,然後把request_sock從半連接隊列裏移除(tcp_check_req->inet_csk_reqsk_queue_removed)
				//具體流程:tcp_v4_hnd_req->tcp_check_req->tcp_v4_syn_recv_sock->tcp_create_openreq_child->inet_csk_clone_lock
				//(newsk->sk_state = TCP_SYN_RECV)
				struct sock *nsk = tcp_v4_hnd_req(sk, skb);
				if (!nsk)
					goto discard;
				//ack時找到的request_sock與sk不是同一個,條件成立
				if (nsk != sk) {
					sock_rps_save_rxhash(nsk, skb);
					//該函數最終調用tcp_rcv_state_process,在tcp_rcv_state_process裏判斷sk->sk_state狀態爲SYNC_RECV,
					//並且收到的是ack報文,然後將狀態置位ESTABLISHED。
					if (tcp_child_process(sk, nsk, skb)) {
						rsk = nsk;
						goto reset;
					}
					return 0;
				}
			} else
				sock_rps_save_rxhash(sk, skb);

			if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
				rsk = sk;
				goto reset;
			}
			return 0;
		}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章