TCP三次握手在linux内核中的实现
TCP三次握手在linux內核中的實現
?
以下基于linux內核2.4.0源碼(轉自www.yuanma.org/)
??? 以前一直使用的網絡通訊的函數都是工作在阻塞模式。在看connect實現源碼時,突然想到tcp/ip的
三次握手在內核如何實現的,尤其是在非阻塞模式下式,涉及到等待對端回送ack包,而本端又要立即返
回,想來這種實現肯定是遵循某種規(guī)則或是將所有的相關函數組合起來。
??? 查看一些網絡通信書籍,可知果然如此。應用編程如果設置為非阻塞模式,則連接時,connect發(fā)送
SYN包后立即返回-EINPROGRESS,表示操作正在處理中;隨后應用可以在connect返回后做一些其它的處理,
最后在select函數中來捕獲socket的連接、讀寫、異常事件以觸發(fā)相關操作,下面我們看看內核中的相關
實現:
一、tcp/ip連接的三次握手過程:
???? ?client??? SYN包--->????? server
???? ?client??? <---ACK包????? server
???? ?client??? ACK包--->????? server
二、客戶端支持
???? client發(fā)送2個包,一個SYN包,一個對服務器的響應ACK包。???? ?
???? client函數調用鏈:connect-->sys_connect->inet_stream_connect->tcp_connect...
???? 看inet_stream_connect中實現的部分代碼段:
???? ...
???? switch (sock->state) {
???? ...????????
??????????????? /*此處調用tcp_connect函數發(fā)送SYN包*/
???? ??err = sk->prot->connect(sk, uaddr, addr_len);???? ??
??if (err < 0)? //出錯則退出
???goto out;
???? ??sock->state = SS_CONNECTING;
??/* 此處僅設置socket的狀態(tài)為SS_CONNECTING表示連接狀態(tài)正在處理;
?? * 不同之處在于非阻塞情況下,返回值設置為-EINPROGRESS表示操作正在處理
?? * 而阻塞式情況則在獲得ACK包后將返回值置為-EALREADY.
?? */
??err = -EINPROGRESS;
??break;
????? }
?????
????? timeo = sock_sndtimeo(sk, flags&O_NONBLOCK); //注意,如果此時設置了非阻塞選項,則timeo返回0
??????? //如果socket對應的sock狀態(tài)是SYN包已發(fā)送或收到SYN包并發(fā)送了ACK包,并等待對端發(fā)送第三此的ACK包
?if ((1<<sk->state)&(TCPF_SYN_SENT|TCPF_SYN_RECV)) {
??/* 錯誤返回碼err前面已經設置 */
??if (!timeo || !inet_wait_for_connect(sk, timeo))
??/*注意上面所判斷的2中情況,1、如果是非阻塞模式,則!timeo為1,則直接跳到out返回-EINPROGRESS結束connect函數
??? 2、若為阻塞模式,則在inet_wait_for_connect函數中通過schedule_timeout函數放棄cpu控制權睡眠,等待服務器端
??? 發(fā)送ACK響應包后被喚醒繼續(xù)處理。如果沒有異常出現,則置socket狀態(tài)為SS_CONNECTED,表示連接成功,正確返回
??*/
???goto out;
??
??err = sock_intr_errno(timeo);
??if (signal_pending(current)) /*處理未決信號*/
???goto out;
?}
?...
?sock->state = SS_CONNECTED;
?err = 0;
???? out:
?release_sock(sk);
?return err;
?...
??? 上面的描述有一個問題:對服務器的響應ACK包是什么時候發(fā)送的?對于非阻塞模式,應該是應用處理過程中
的某個異步時間;對于阻塞模式,則是在inet_wait_for_connect函數中睡眠時處理。
??? 即網卡在收到對方的ack包后,上傳給對應的socket時發(fā)送服務器的響應ACK包
??? 函數調用鏈為:netif_rx-->net_rx_action-->...(IP層處理)-->tcp_v4_rcv-->tcp_v4_do_rcv-->
??? ??tcp_rcv_state_process-->tcp_rcv_synsent_state_process-->tcp_send_synack-->tcp_transmit_skb...
??? 發(fā)送SYN包后,socket對應的sock的狀態(tài)變成TCPF_SYN_SENT,網卡收到服務器的ack傳到tcp層時,根據TCPF_SYN_SENT
??? 狀態(tài),做相關判斷后再發(fā)送用于第三次握手的ack包。至此,將socket的狀態(tài)改為連接建立,即TCP_ESTABLISHED。
??? 具體的代碼大家可以根據我提供的函數調用鏈查看。
??? 注意,以TCPF_前綴開頭的狀態(tài)都表示是中間狀態(tài),而已TCP_為前綴的狀態(tài)才是socket的一個相對穩(wěn)定的狀態(tài)。?? ??
???
??? 這里有一個疑問,,根據接收處理源碼,先前的SYN包應該發(fā)送給服務器的監(jiān)聽socket,而第三次握手似乎應該發(fā)送給
??? 連接(未真正連接,因為三次握手還沒完成呢)的socket,這個問題有待進一步確認
三、服務器端支持
??? 服務器端此時必須是監(jiān)聽狀態(tài),則其函數調用鏈為:
????????? netif_rx-->net_rx_action-->...(IP層處理)-->tcp_v4_rcv-->tcp_v4_do_rcv-->
????????? tcp_rcv_state_process-->tcp_v4_conn_request-->tcp_v4_send_synack...
??? 在tcp_v4_conn_request,中部分代碼如下:?????
??? ...
??? case TCP_LISTEN:
??if(th->ack) /*監(jiān)聽時收到的ack包都丟棄?*/
???return 1;
??if(th->syn) {/*如果是SYN包,則調用tcp_v4_conn_request*/
???if(tp->af_specific->conn_request(sk, skb) < 0)
????return 1;
??? ...
???
四、后記
??? 這里只是給了一個tcp/ip三次握手原理的實現框架簡述,結合相關理論教材和這個流程,仔細閱讀
linux內核對tcp/ip的實現,對自己理解tcp/ip的精髓不無裨益,期待著與大家一起進步。
總結
以上是生活随笔為你收集整理的TCP三次握手在linux内核中的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux网络编程——浅谈 TCP 三次
- 下一篇: Linux内核分析 - 网络[十六]:T