linux kernel 三次握手建立TCP链接的实现
1. 應用層
1.1 server
socket() -> bind() -> listen() -> accept() -> recv() & send()
1.2 client
socket() ->? connect() -> send() & recv()
1.3 三次握手建立連接
(1) 客戶端發送一個 SYN 段(SYN 標志位置位),包含初始序號 ISN,在圖中,這個序號的值 seq = 2379453243. 在這個過程中,客戶端是通過 connect 函數發起連接請求的,此時 connect 函數阻塞,等待服務器發回 ACK 應答。
(2) 服務器端接收到 SYN 段后(通過listen監聽),知道有新的連接請求到來,于是初始化一個序號 ISN,在上面的例子中這個值是 seq = 4269857. 此時服務器創建一個 TCP 段,將 SYN 和 ACK 標志置位,讓 seq = 4269857, ack = 2379453244,然后將這個 TCP 段發送給客戶端。這個步驟完全有內核完成,并將此連接加入未完成連接隊列。
(3) 客戶端再次收到服務器發送來的 TCP 段后,檢查到帶有 SYN 和 ACK 標志,于是客戶端一方連接已經建立成功,此時 connect 函數返回。客戶端創建一個 TCP 段,將 ACK 標志置位,同時將 ack 的值設置為 4269858,發送給服務器。
(4) 服務器收到客戶端的 ACK 后,將此連接移到已完成連接隊列(accept)。
2. kernel流程
2.1?client 通過connect()發送SYN報文,向服務器發起tcp連接
connect ---->?SYSCALL_DEFINE3(__sys_connect)(socket.c) -> sock->ops->connect ->?inet_stream_connect(af_inet.c) ->?__inet_stream_connect ->?sk->sk_prot->connect ->?tcp_v4_connect(tcp_ipv4.c) ->?tcp_connect(tcp_output.c) ->?tcp_send_syn_data | tcp_transmit_skb(syn)
tcp_connect(): Build a SYN and send it off
對于阻塞調用,等待后續握手的完成;對于非阻塞調用,則直接返回 -EINPROGRESS,然后在select函數中來捕獲socket的連接、讀寫、異常事件以觸發相關操作。
2.2?tcp_rcv_state_process(三次握手狀態機)
網卡驅動 --> netif_receive_skb - --> ip_rcv ---> ip_local_deliver_finish ---> tcp_v4_rcv -> tcp_v4_do_rcv -> tcp_rcv_state_process
1) client
客戶端收到SYN+ACK報文,然后回ACK報文
case TCP_SYN_SENT: tcp_rcv_synsent_state_process ->?tcp_finish_connect & tcp_send_ack
client tcp state: CLOSING ->? TCP_SYN_SENT ->?TCP_ESTABLISHED
2) server
a. 收到客戶端SYN報文, 發送SYN+ACK報文
case?TCP_LISTEN:?icsk->icsk_af_ops->conn_request ->?tcp_v4_conn_request ->?tcp_conn_request ->?af_ops->send_synack ->?tcp_v4_send_synack
tcp_v4_send_synack():?Send a SYN-ACK after having received a SYN.
server tcp state變為TCP_NEW_SYN_RECV
b. 收到客戶端ACK報文
連接已經建立,喚醒阻塞的accept函數。
首先在tcp_v4_rcv中,建一個新的sock進入TCP_SYN_RECV狀態;
然后調用tcp_child_process ->?tcp_rcv_state_process,最終進入TCP_ESTABLISHED狀態,并放入accept隊列通知select/epoll。
server tcp state:? CLOSING ->??TCP_LISTEN ->? TCP_NEW_SYN_RECV ->?TCP_SYN_RECV ->? TCP_ESTABLISHED
2.3? server通過listen操作開始監聽,此時就可以接受到client連接請求。
listen ---->?SYSCALL_DEFINE2(listen) ->?__sys_listen ->?sock->ops->listen --->? inet_csk_listen_start
2.4 accept
accept()實際要做的事件并不多,它的作用是返回一個已經建立連接的socket(即經過了三次握手),這個過程是異步的,accept()并不親自去處理三次握手過程,而只是監聽icsk_accept_queue隊列,當有socket經過了三次握手,它就會被加到icsk_accept_queue中,所以accept要做的就是等待隊列中插入socket,然后被喚醒并返回這個socket。而三次握手的過程完全是協議棧本身去完成的。換句話說,協議棧相當于寫者,將socket寫入隊列,accept()相當于讀者,將socket從隊列讀出。這個過程從listen就已開始,所以即使不調用accept(),客戶仍可以和服務器建立連接,但由于沒有處理,隊列很快會被占滿。
2.5 bind
bind操作的主要作用是將創建的socket與給定的地址相綁定,這樣創建的服務才能公開的讓外部調用。當然對于socket服務器的創建來說,這一步不是必須的,在listen()時如果沒有綁定地址,系統會選擇一個隨機可用地址作為服務器地址。
總結
以上是生活随笔為你收集整理的linux kernel 三次握手建立TCP链接的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 兔子的长耳朵除了能“耳听八方”,还有什么
- 下一篇: 当代才子!14岁初中生保送清华本硕博连读