TCP/IP網絡協議總結(二)傳輸層協議之TCP連接的建立與斷開


      前面已經介紹了了TCP/IP協議棧,網絡數據幀/報/段結構,TCP連接等網絡通訊基礎知識,這一篇文章我來總結一下TCP協議:
1.TCP的連接的建立
2.TCP三次握手
  握手為什么是3次而不是2次或4次;
  SYN攻擊;
3.TCP斷開連接,四次揮手;
  TIME_WAIT狀態;
 
1.TCP三次握手和四次揮手的過程:
               
               
三次握手的過程:
       1次握手:由客戶端主動發起握手請求,將TCP報文頭部SYN標志位置為1,隨機產生一個序號seq=x,發送第一個SYN包給服務器端,客戶端從CLOSE狀態進入SYN_SENT狀態,等待server確認。
       2次握手:server端收到來自客戶端的數據包后,解析知道了客戶端要建立TCP連接的請求,server端將TCP報文頭部標志位SYN和ACK都置為1,ack=x+1;並且隨機產生一個seq=y,並將數據包發給客戶端,server端從LISTEN狀態進入SYN_RCVD狀態。
       3次握手:客戶端收到server端返回的確認后,校驗ack=x+1值正確,客戶端將標志位ACK置為1,ack=y+1,並把數據包發給server, server端收到ACK包后先校驗ack=y+1;如果校驗成功,那么成功建立TCP連接;
ps:三次握手的數據包被稱為握手包,是由操作系統來處理的
 
四次揮手:正常斷開一個TCP連接。tcp連接是全雙工通信,所以通信雙方每一個方向的接收/發送都需要單獨進行關閉。   
主動斷開TCP連接有可能是server端,也有可能是客戶端;正常斷開連接是由客戶端主動發起的
       1次揮手:客戶端向server發送一個FIN包,將TCP報文頭部FIN狀態位置1,向server發送出FIN包后客戶端進入FIN_WAIT1;
       2次揮手:server收到來自客戶端發的FIN包,回復客戶端,將TCP報文頭部ACK狀態位置1,server端由ESTABLISHED狀態進入CLOSE_WAIT狀態,這時如果server有數據可以繼續正常發送,只是不再接收數據了。
       3次揮手:當客戶端收到ACK包后,客戶端從FIN_WAIT1進入到FIN_WAIT2狀態。第3次揮手是當server端沒有要發送的數據時,將TCP報文頭部FIN狀態位置1,向客戶端發送FIN包,server端從CLOSE_WAIT狀態進入到LAST_WAIT狀態。
       4次揮手:客戶端收到server的FIN包后,將TCP報文頭部ACK狀態位置為1,向server端發送ACK包,由此client客戶端從FIN_WAIT2進入到TIME_WAIT狀態。server端收到ACK包的時候,進入到CLOSE狀態,server端連接正常關閉,2MSL后,客戶端正常進入CLOSE狀態,client端連接正常關閉。
只有當客戶端主動關閉連接時才會有TIME_WAIT狀態。
 
2.TCP連接建立的原理,探索三次握手細節
       TCP連接的建立:(也是三次握手)
1.TCP是面向連接的傳輸層協議,建立的連接不是物理的,建立的連接是邏輯上的連接,這個過程也就是TCP三次握手。
2.三次握手:服務器端處於listen監聽狀態,客戶端要想和服務器建立連接connect(),首先要發送一個數據包,這個握手的控制數據包由傳輸控制層產生的,服務器端收到握手包會返回一個syn+ack包,客戶端再回復一個ack包,這三個由通信兩端內核里的傳輸控制層產生的數據包被稱為三次握手,與應用軟件無關。
3.三次握手可以讓通信雙方都確認自己是通的,然后雙方的操作系統內核為各自應用程序開辟資源。
三次握手之后雙方內存里開辟資源開始為應用程序提供服務,這時就有上面的連接了。在這里開辟的資源是物理實際存在的,而連接是沒有通道的。

用來描述一個連接的狀態信息組合{Socket、序列號和滑動窗口大小},這個狀態信息組合就可以被稱為是一個連接。
       socket: socket五元組{sIP,sport,dIP,dport,protocol}表示網絡中唯一的一個網絡進程。
       序列號:解決TCP亂序的問題。
       滑動窗口大小:負責通訊中流量控制。
 socket五元組就是通過sIP+sport,dIP+dport還有協議包來標識一個網絡通信。協議號就是TCP/UDP,在IP協議頭部就會有協議號這個字段,8位協議號是6時標識選擇TCP傳輸層協議,協議號是17時表示選擇UDP;
                 
 
Linux操作系統中用來查看TCP連接的狀態的命令
> netstat -napt   
 
2.1:握手為什么是3次而不是2次或4次
      1.三次握手可以避免歷史連接的初始化;
      TCP的確認機制,TCP建立連接時第二次握手,客戶端會收到server的ack_num,如果ack序列號校驗ack已超時作廢,那么當前收到ack序列號的連接是由於網絡環境擁堵等影響的歷史連接。如果客戶端對server發送的ack序列號校驗失敗,將TCP報文頭部RST標志位置為1發送給server端中止連接。
     2.握手需要三次才能同步通信雙方的初始序列號;也和TCP的確認機制有關,用來確認數據報文成功有序的被通信對方收到,而確認機制會導致服務器通信以外的額外開銷;
     3.當在syn-ack兩次握手建立連接的場景下,客戶端主動給server發送SYN報文請求建立連接,當SYN包在網絡環境中堵塞時,客戶端遲遲收不到ACK包,會超時重傳繼續向server發送SYN包。同時,在有多個SYN包的情況下,server回復完第一個ACK包后,並不能確定連接是否已經成功建立,server會只要繼續收到SYN包都會認為是一個新的連接,這樣就會建立多個冗余的無效鏈接,造成不必要的資源浪費。
 
2.2:SYN攻擊
       SYN攻擊是在TCP連接的建立三次握手期間,攻擊者在短時間內偽造大量不同IP向server發送SYN包,server收到SYN包回復ACK+SYN包,但卻無法收到來自客戶端的ACK包數據,造成大量半連接狀態的fd,SYN半連接隊列將會滿,導致server無法正常為其他真正的網絡客戶端提供服務。
 
SYN攻擊過程圖解:
          正常建立連接的流程詳細分析如下:
                    
 
      圖一:正常建立連接:
              
 
  圖二:由於應用程序處理慢導致accept隊列占滿:
             
 
  圖三:客戶端惡意發送SYN包導致的SYN隊列占滿:
     
 
2.2.1.避免SYN解決方案一:
      很容易想到一個解決方案:通過修改linux內核參數,控制隊列的大小,當隊列滿了的話采取處理措施。
      1.網卡接收數據包的速度大於內核處理的速度時,會有一個隊列,可設置其長度最大參數:net.core.netdev_max_backlog;
      2.SYN_RECV狀態連接最大數的參數:net.ipv4.tcp_max_syn_backlog
      3.對超出處理能力之外的SYN新連接,返回RST,server丟棄該連接;設置參數:net.ipv4.tcp_abort_on_overflow
 
RST狀態位:當建立連接時,通信雙方任意一方發現異常,而中止連接;分為兩種情況,
  1.客戶端發起RST包中止連接,當client發送SYN包請求建立連接seq=87,由於網絡堵塞而沒有收到server發送的ACK+SYN包,會再次發送新的SYN包seq=117;由於網絡環境的影響,舊的SYN包seq=87先到,server發送SYN+ACK包seq=222,ack=88.這個時候client如果收到的AYN+ACK包seq=任意值;ack=118就可以正常建立連接,這樣就會導致實際上收到的包校驗錯誤,有client發送RST包中止連接。
  2,服務器端發起RST包中止連接,如上解決SYN攻擊的解決方案一中所述,server端對超出處理能力之外的SYN新連接,返回RST,server丟棄該連接;
  
         
 2.2.2.避免SYN攻擊的解決方案二
 
  如圖四:
     
 
設置linux內核參數:net.ipv4.tcp_syncookies = 1;
當SYN隊列被占滿時啟動cookie,具體處理流程如下:
       1,當 SYN 隊列滿之后,后續服務器收到 SYN 包,不進入SYN 隊列;
       2,計算出一個 cookie 值,再以 SYN + ACK 中的序列號返回客戶端;
       3,服務端接收到客戶端的應答報文時,服務器會檢查這個 ACK 包的合法性。如果合法,直接放入到Accept 隊列;
       4,最后應用通過調用 accpet() socket 接口,從 Accept 隊列取出的連接。
 
3.TCPl連接中端口號原理
端口號體現在同一台機器開啟多個網絡進程的場景下:
  mac:數據鏈路層的地址,用來表示同一鏈路中不同的機器;
  IP:IP層中的的地址,用來標識TCP/IP網絡中互聯的主機和路由器;
  端口號:傳輸層地址;用來識別同一台機器中正在通信的不同的網絡進程(即正在通信中的不同應用程序);
 
 如下圖為例:
                  
   兩個客戶端采用web瀏覽器服務器進行通信,通信中采用TCP傳輸層協議,http應用層協議。當客戶端1:192.168.1.114:23535和192.168.1.107:23535同時訪問server端120.94.23.180:80時客戶端IP地址成了server端區別客戶端網絡進程的標識;當客戶端2的兩個網絡進程同時訪問server端的80端口時,IP相同,這時server端就根據客戶端的端口號來標識區分這兩個網絡進程。
 
端口號分布:
1.標准固定的端口號:分布范圍:0-1023。靜態分配。每個應用程序的端口號固定,其他應用程序不可隨意使用。比如http 80端口。
2.被正式注冊的端口號:分布范圍:1024-49151。這個范圍內的端口號可用於任意通信中。
3.時序分配端口號:分布范圍:49152-65535。客戶端與服務器端通信時,客戶端無需綁定端口號,而是完全有操作系統隨機動態分配端口號。
 
4.TCP斷開連接,探索4次揮手細節
     Q:斷開連接為什么需要四次揮手:
        TCP是一種面向連接的全雙工的通信方式,因此斷開連接需要雙方分別發送各自的FIN包和ack確認包后,在雙方都確認通信對方可讀可寫都已經關閉才正常斷開TCP連接,釋放系統資源。三次握手中第二次握手時,server發送ACK+SYN一次發送給客戶端,在四次揮手斷開連接時FIN和ACK需要分開發送,因此揮手需要四次,其實通信雙方協商分別關閉通信狀態中自己的讀和寫。
 
  4.1.TCP的TIME_WAIT狀態的作用
  
 一般由cliect主動關閉方,收到server發送的FIN,就會陷入TIME_WAIT狀態,保證網絡延時的數據包可以被正常的而接收到;TIME_WAIT狀態有兩個作用:
       1.確保斷開連接前的網絡上的數據包自然消失;保證下次通信網絡中的數據包都是新連接中產生的。
       2.保證了TCP連接正常斷開,不影響下一次的連接。
 
TIME_WAIT時長:MSL默認時長是30s;2MSL,默認時長是60s。
       2MSL表示網絡中報文從發送到確認丟失的時間長。在TCP斷開連接的過程中,client收到FIN包后從發送ACK包開始計時,如果client在TIME_WAIT狀態2MSL內,因為網絡環境的問題沒有傳輸到server,由於TCP的超時重傳機制,server會繼續發送FIn包給client,client收到FIn包后發送ACK包,2MSL會被清零重新計時,直到確認server收到ACK包正常斷開連接。
 
TCP的TIME_WAIT狀態作用一:
       防止這次TCP通信的端口號的數據,因為網絡環境的因素導致,下一次在這個端口上建立的連接,接收到歷史數據包,產生數據錯亂的問題。
 
                       
        如果TIME_WAIT,時間小於2MSL,或者沒有TIME_WAIT狀態時,如圖,seq=781這個數據包因為網絡堵塞或其他網絡環境因素導致client沒有接收成功,下一次三次握手建立連接后通信時,采用了相同的TCP端口進行通信,網絡堵塞的數據包seq=781可能被client正常接收,導致此次通信中數據錯亂的問題.
                      
 
TCP的TIME_WAIT狀態作用二:
        client發送完ACK包,沒有TIME_WAIT狀態直接進入CLOSE狀態,如果ACK丟失,server沒有收到ACK包,將一直在LAST_WAIT狀態下,無法正常關閉;下一次有client發送新的請求建立連接時,server不具備有處理請求的能力,直接返回RST包,將連接終止丟棄。TIME_WAIT狀態等待2MSL后,即時ACK丟棄,server沒有正常收到ACK包;由於超時重傳機制,server會重新發送FIN包,直到server收到ACK包正常斷開連接,釋放系統資源。
                      
 
  4.2.TCP的TIME_WAIT狀態的優化:
 server維持過多TIME_WAIT狀態將要面臨的問題:       
       試想在網絡高並發的場景下,如果server維持過多的TIME_WAIT這種半斷開狀態的TCP連接時,會導致內存資源得不到釋放,還有端口資源占用的問題,當有限的端口資源(32768~65535,可以通過linux內核參數的設置:net.ipv4.ip_local_port_range 的值)被全部占用時,導致無法創建新的連接。
 
TCP的TIME_WAIT狀態的優化有三種方法:
1.通過對linux內核參數的設置打開Linux內核TCP時間戳功能和連接復用功能;
       在client端設置:
       net.ipv4.tcp_timestamps=1;
       net.ipv4.tcp_tw_resue=1;
       在TCP頭部打開時間戳功能記錄發送端發送數據包時的時間戳和從對端接收該數據包的最新時間戳,一旦發現時間戳過期數據包會被丟棄,有效的解決了2MSL的問題;
       在client端設置net.ipv4.tcp_tw_resue=1;  當client端調用connect();請求建立連接時,內核會隨機找一個time_wait狀態超過1s的連接給新建立的連接復用。
下面兩種方法不推薦使用,只做簡單介紹:
2.設置內核參數:net.ipv4.tcp_max_tw_buckets,默認值為18000,限制系統維持TIME_WAIT狀態的連接數量,當超過默認值時,系統會將后面的TIME_WAIT連接狀態重置。
3.在程序中使用SO_LINGER來設置socket選項,發送RST包,跳過四次揮手直接關閉。
       struct linger so_linger;
       so_linger.l_onoff=1;
       so_linger.l_linger=0;
       setsockopt(s,SOL_SOCKET,SO_LINGER,&so_linger,sizeof(so_linger));
 
  4.3.異常斷開連接的檢測--保活機制Keepalive
 
TCP斷開連接有三種情況:
       1.網絡健康,軟件崩潰導致連接斷開;
       2.正常斷開連接,客戶端正常退出;
       3.軟件正常,網絡異常導致連接斷開;
保活機制:
       功能:心跳機制用來監測客戶端或者server是否正常
       在Linux內核TCP/IP協議棧中自帶了心跳機制的功能,其中的內核參數可以根據實際開發的需要進行修改;
       net.ipv4.tcp_keepalive_time=7200
       net.ipv4.tcp_keepalive_intvl=75
 
       net.ipv4.tcp_keepalive_probes=9
 
       限制在一段時間內(2h),如果客戶端與服務器端沒有任何數據交互,啟用心跳機制;對於server來說,給每一個監聽的clientfd加一個定時器,在定時器定時內有任何一次數據交互,定時器都會被清零重置。默認參數中超過2h,啟動保活機制,server向該client發送試探包,也叫心跳包,是個空包。如果收到響應定時器清零重置,否則每隔75s會再次發送試探,會發9次,如果任意一次有相應定時器重置。9次以后client任然沒有任何響應,server主動斷開連接,釋放系統資源。
 
5.TCP的12種狀態遷移圖,TCP狀態機
                  
 
6.傳輸層協議之UDP與TCP特點
UDP傳輸層協議本身不提供復雜的控制機制,是面向無連接的傳輸層協議;
        如果收到應用層要發送的數據時,立即發送到網絡上。單次socket sendto()數據大小受限於UDP頭16位數據包長度65535的限制,如果UDP數據包長度超過這個值,sendto()失敗,返回值為-1(返回0,表示發送成功)
       
        UDP的特點:如果網絡堵塞的場景下,UDP不能進行流量控制和避免網絡擁塞的操作;並且傳輸過程中會出現丟包亂序的問題。如果實際開發中需要以上細節處理,交給UDP的應用層程序去處理。
可靠UDP協議的設計:參考TCP可靠的特性主要是通過序列號,確認應答機制,重發機制,連接管理,滑動窗口控制等機制來實現的。
        
       TCP的特點:面向連接;通訊雙向性—TCP是全雙工通信協議;支持多連接和多點標識(TCP連接由相連接的一對套接字來表示);可靠的傳輸層協議;確認機制;流式傳輸數據流(流式傳輸;非結構化數據;數據流管理);支持流量控制管理。
TCP是基於字節流傳輸的傳輸層協議。
       TCP 是一個工作在傳輸層的可靠數據傳輸的服務,它能確保接收端接收的網絡包是無損壞、無間隔、非冗余和按序的。
 
如何保證UDP的可靠:
seq序列號:解決網絡數據包不亂序的問題;
       在建立連接時由計算機生成的隨機數作為其初始值,通過 SYN 包傳給接收端主機,每發送一次數據,seq的大小就「累加」一次。
ack應答序號:來解決不丟包的問題;
       指下一次收到的數據的序列號[期望值],發送端收到這個確認應答以后可以認為在這個序號以前的數據都已經被正常接收。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM