linux kernel 三次握手建立TCP链接的实现
1. 應(yīng)用層
1.1 server
socket() -> bind() -> listen() -> accept() -> recv() & send()
1.2 client
socket() ->? connect() -> send() & recv()
1.3 三次握手建立連接
(1) 客戶端發(fā)送一個 SYN 段(SYN 標(biāo)志位置位),包含初始序號 ISN,在圖中,這個序號的值 seq = 2379453243. 在這個過程中,客戶端是通過 connect 函數(shù)發(fā)起連接請求的,此時 connect 函數(shù)阻塞,等待服務(wù)器發(fā)回 ACK 應(yīng)答。
(2) 服務(wù)器端接收到 SYN 段后(通過listen監(jiān)聽),知道有新的連接請求到來,于是初始化一個序號 ISN,在上面的例子中這個值是 seq = 4269857. 此時服務(wù)器創(chuàng)建一個 TCP 段,將 SYN 和 ACK 標(biāo)志置位,讓 seq = 4269857, ack = 2379453244,然后將這個 TCP 段發(fā)送給客戶端。這個步驟完全有內(nèi)核完成,并將此連接加入未完成連接隊(duì)列。
(3) 客戶端再次收到服務(wù)器發(fā)送來的 TCP 段后,檢查到帶有 SYN 和 ACK 標(biāo)志,于是客戶端一方連接已經(jīng)建立成功,此時 connect 函數(shù)返回。客戶端創(chuàng)建一個 TCP 段,將 ACK 標(biāo)志置位,同時將 ack 的值設(shè)置為 4269858,發(fā)送給服務(wù)器。
(4) 服務(wù)器收到客戶端的 ACK 后,將此連接移到已完成連接隊(duì)列(accept)。
2. kernel流程
2.1?client 通過connect()發(fā)送SYN報(bào)文,向服務(wù)器發(fā)起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
對于阻塞調(diào)用,等待后續(xù)握手的完成;對于非阻塞調(diào)用,則直接返回 -EINPROGRESS,然后在select函數(shù)中來捕獲socket的連接、讀寫、異常事件以觸發(fā)相關(guān)操作。
2.2?tcp_rcv_state_process(三次握手狀態(tài)機(jī))
網(wǎng)卡驅(qū)動 --> netif_receive_skb - --> ip_rcv ---> ip_local_deliver_finish ---> tcp_v4_rcv -> tcp_v4_do_rcv -> tcp_rcv_state_process
1) client
客戶端收到SYN+ACK報(bào)文,然后回ACK報(bào)文
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報(bào)文, 發(fā)送SYN+ACK報(bào)文
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變?yōu)門CP_NEW_SYN_RECV
b. 收到客戶端ACK報(bào)文
連接已經(jīng)建立,喚醒阻塞的accept函數(shù)。
首先在tcp_v4_rcv中,建一個新的sock進(jìn)入TCP_SYN_RECV狀態(tài);
然后調(diào)用tcp_child_process ->?tcp_rcv_state_process,最終進(jìn)入TCP_ESTABLISHED狀態(tài),并放入accept隊(duì)列通知select/epoll。
server tcp state:? CLOSING ->??TCP_LISTEN ->? TCP_NEW_SYN_RECV ->?TCP_SYN_RECV ->? TCP_ESTABLISHED
2.3? server通過listen操作開始監(jiān)聽,此時就可以接受到client連接請求。
listen ---->?SYSCALL_DEFINE2(listen) ->?__sys_listen ->?sock->ops->listen --->? inet_csk_listen_start
2.4 accept
accept()實(shí)際要做的事件并不多,它的作用是返回一個已經(jīng)建立連接的socket(即經(jīng)過了三次握手),這個過程是異步的,accept()并不親自去處理三次握手過程,而只是監(jiān)聽icsk_accept_queue隊(duì)列,當(dāng)有socket經(jīng)過了三次握手,它就會被加到icsk_accept_queue中,所以accept要做的就是等待隊(duì)列中插入socket,然后被喚醒并返回這個socket。而三次握手的過程完全是協(xié)議棧本身去完成的。換句話說,協(xié)議棧相當(dāng)于寫者,將socket寫入隊(duì)列,accept()相當(dāng)于讀者,將socket從隊(duì)列讀出。這個過程從listen就已開始,所以即使不調(diào)用accept(),客戶仍可以和服務(wù)器建立連接,但由于沒有處理,隊(duì)列很快會被占滿。
2.5 bind
bind操作的主要作用是將創(chuàng)建的socket與給定的地址相綁定,這樣創(chuàng)建的服務(wù)才能公開的讓外部調(diào)用。當(dāng)然對于socket服務(wù)器的創(chuàng)建來說,這一步不是必須的,在listen()時如果沒有綁定地址,系統(tǒng)會選擇一個隨機(jī)可用地址作為服務(wù)器地址。
總結(jié)
以上是生活随笔為你收集整理的linux kernel 三次握手建立TCP链接的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 兔子的长耳朵除了能“耳听八方”,还有什么
- 下一篇: linux 其他常用命令