TCP三次握手詳解

TCP三次握手

1.TCP特性

  1. 說明:

    1. TCP提供一種面向連接的、可靠的字節流服務
    2. 在一個TCP連接中,僅有兩方進行彼此通信。廣播和多播不能用於TCP
    3. TCP使用校驗和,確認和重傳機制來保證可靠傳輸
    4. TCP給數據分節(給每一個傳送的數據字節都編號)進行排序,並使用累積確認保證數據的順序不變和非重複
    5. TCP使用滑動窗口機制來實現流量控制,通過動態改變窗口的大小進行擁塞控制
  2. 注意:

    TCP 並不能保證數據一定會被對方接收到,因爲這是不可能的。TCP 能夠做到的是,如果有可能,就把數據遞送到接收方,否則就(通過放棄重傳並且中斷連接)通知用戶。因此準確說 TCP 也不是 100% 可靠的協議,它所能提供的是數據的可靠遞送或故障的可靠通知。

2.TCP首部

  1. 說明:

    1. TCP(Transmission Control Protocol,傳輸控制協議)是一種面向連接的、可靠的、基於字節流的通信協議,數據在傳輸前要建立連接,傳輸完畢後還要斷開連接。
    2. 客戶端在收發數據前要使用 connect() 函數和服務器建立連接。建立連接的目的是保證IP地址、端口、物理鏈路等正確無誤,爲數據的傳輸開闢通道。
    3. TCP建立連接時要傳輸三個數據包,俗稱三次握手(Three-way Handshaking)。

    TCP/IP

    • 解釋:

      1. 序號:seq(Sequence)序號,佔32位,用來標識從TCP源端向目的端發送的字節流,發起方發送數據時對此進行標記。
      2. 確認序號:ack(Acknowledge)序號,佔32位,只有ACK標誌位爲1時,確認序號字段纔有效,ack=seq+1。
      3. 標誌位:每個標誌位佔用1Bit,共6個,即URG、ACK、PSH、RST、SYN、FIN等,具體含義如下:

        1. URG:緊急指針(urgent pointer)有效。
        2. ACK(Acknowledge):確認序號有效。
        3. PSH(Push):接收方應該儘快將這個報文交給應用層。
        4. RST(Reset):重置連接。
        5. SYN(Synchronous):發起一個新連接。
        6. FIN(Finish):釋放一個連接。
      4. 需要注意的是:

        1. 不要將確認序號 ack(表示確認信息)與標誌位中的 ACK(爲1表示確認有效)搞混了。
        2. 確認方 ack等於發起方 seq+1,兩端配對。

3.三次握手

  1. 說明:

    1. 所謂三次握手(Three-way Handshake),是指建立一個 TCP 連接時,需要客戶端和服務器總共發送3個包。
    2. 三次握手的目的是連接服務器指定端口,建立 TCP連接,並同步連接雙方的序列號和確認號,交換 TCP窗口大小信息。在 socket 編程中,客戶端執行 connect() 時。將觸發三次握手。

    三次握手

  2. 解釋:

    1. 第一次握手(SYN=1, seq=x):

      1. 客戶端發送一個 TCP的 SYN 標誌位置1的包,指明客戶端打算連接的服務器的端口(請求同步),並選擇序號 seq=x,表明傳送數據時的第一個數據字節的序號是 x。(seq是個隨機值)
      2. 發送完畢後,客戶端進入 SYN_SEND 狀態。
    2. 第二次握手(SYN=1, ACK=1, seq=y, ack=x+1):

      1. 服務器的TCP收到連接請求報文段後,如同意,則發回確認包(ACK)應答。即 SYN 標誌位和 ACK 標誌位均爲1。服務器端選擇自己 ISN 序列號(隨機值y),放到seq 域裏,同時將確認序號ack設置爲客戶的 ISN 加1,即 x+1(即回覆對方確認收到了序列號爲x開始的包,且希望下次的數據從x+1的位置開始)。
      2. 發送完畢後,服務器端進入 SYN_RCVD 狀態。
    3. 第三次握手(ACK=1,ack=y+1,seq=x+1)

      1. 客戶端收到此報文段後再次發送確認包(ACK),SYN 標誌位爲0,ACK 標誌位爲1,並且把服務器發來 ACK 的序號字段+1,放在確定字段ack中發送給對方,並且告訴服務器自己的seq=x+1
      2. 發送完畢後,客戶端進入 ESTABLISHED 狀態,當服務器端接收到這個包時,也進入 ESTABLISHED 狀態,TCP握手結束。
  3. 問題解決:

    1. 爲什麼初始seq要取隨機值呢?

      1. 首先這個隨機值並不是隨機的,而是可以預測的。
      2. 其次,它一般基於時鐘產生,在rfc793中講到“The generator is bound to a (possibly fictitious) 32 bit clock whose low order bit is incremented roughly every 4 microseconds.”大概就是每 4ms加1,這樣ISN循環一次需要4.55小時,而一個連接中傳送的 segment在網絡中存在的最大時間小於4.55小時。如我們需要 segmentseq爲1,則至少4.55小時前的segmentseq纔可能爲1,而segment不可能在網絡中存在4.55小時,所以如果我們接收到seq爲 1 的segment則必然是我們需要的segment(惡意攻擊除外^_^)。
      3. 這樣可以防止上一次連接產生的segment被本次連接錯誤接收,同時也可以從某種程度上防止其它用戶惡意攻擊。
    2. 爲什麼要三次握手,而不是兩次握手或者四次握手

      1. 爲了防止已失效的連接請求報文段突然又傳送到了服務端,因而產生錯誤或者說是解決網絡中存在延遲的重複分組的問題。

        1. 已失效的連接請求報文段的產生在這樣一種情況下:client發出的第一個連接請求報文段並沒有丟失,而是在某個網絡結點長時間的滯留了,以致延誤到連接釋放以後的某個時間纔到達server
        2. 本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段後,就誤認爲是client再次發出的一個新的連接請求。於是就向client發出確認報文段,同意建立連接。
        3. 假設不採用“三次握手”,那麼只要server發出確認,新的連接就建立了。由於現在client並沒有發出建立連接的請求,因此不會理睬server的確認,也不會向server發送數據。但server卻以爲新的運輸連接已經建立,並一直等待client發來數據。這樣,server的很多資源就白白浪費掉了。
      2. 因爲在上述的三次握手中已經確認並建立了連接,那麼就沒有必要去進行第四次握手,這樣可以節約資源。

  4. 補充:

    1. 半鏈接隊列

      1. 半連接隊列:

        1. 在三次握手協議中,服務器維護一個半連接隊列,該隊列爲每個客戶端的SYN包開設一個條目(服務器端在接收到SYN包時,就已經創建了request_sock結構,存儲在半連接隊列中),該條目表明服務器已收到SYN 包,並向客戶發出確認,正在等待客戶的確認包。這些條目所識別的連接在服務器處於SYN_RCVD狀態,當服務器收到客戶端的確認包時,刪除該條目,服務器進入ESTABLISHED狀態。
        2. 未連接隊列的大小爲max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog),也就是可以在說未連接隊列的大小可以在/proc/sys/net/ipv4/tcp_max_syn_backlog中修改配置,如果服務器經常出現過載,可嘗試增加這個數字(tcp_max_syn_backlog)
      2. 半連接(half-open connect)存活時間:

        1. 是指半連接隊列的條目存活的最大時間,也即服務器端從收到SYN包確認這個報文無效的最長時間,該時間值是所有重傳請求包的最長等待時間總和,有時我們也稱半連接存活時間爲Timeout時間、SYN_RCVD存活時間。
    2. 完全連接隊列

      1. 完全連接隊列:

        1. 在第三次握手時,當Server接收到ACK報之後,會進入一個新的叫ACCEPT的隊列,該隊列的長度爲min(backlog, /proc/sys/net/core/somaxconn),默認情況下,somaxconn的值爲128,表示最多有128個ESTABLISHED的連接等待accept(),而backlog值則是由int listen(int sockfd, int backlog)中的第二個參數指定(指定的backlog與半連接狀態的backlog無關係),listen裏面的backlog可以由我們的程序去指定。
        2. 當服務器綁定、監聽某個端口後,這個端口的SYN隊列和ACCEPT隊列就建立好了。
      2. 半連接隊列未滿但是全連接隊列已滿:

        1. 客戶端發出SYN分節,服務器端收下SYN分節並向客戶端發送SYN+ACK,客戶端收到服務器端SYN+ACK後,成爲ESTABLISHED狀態,並向服務器端發送第三次握手ACK,服務器端收到ACK後發現全連接隊列已滿,默認情況下服務器端什麼也不做,狀態依然是SYN_RCVD,此時ListenOverflows+1
        2. 同時服務器端通過對目錄:/proc/sys/net/ipv4/tcp_abort_on_overflow進行修改來決定如何返回,0表示直接丟棄,1表示發送RST通知客戶端(ListenOverflows默認爲0,當全連接隊列超過上限時,ListenOverflow+1),客戶端會重傳SYN(客戶端第一次握手發起的請求)和ACK(客戶端第三次握手期間發出的確認),並且內核會限制SYN隊列的處理速度,如果在SYN隊列中收到太多的SYN,服務器端將會丟棄一些,這樣丟棄的SYN對應的客戶端需要重發SUN包,當達到一定的閾(yù)值(可以理解爲連接被動打開方的確認連接的應答最大重試數,即對於一個新建連接,內核需要發送多少SYN連接請求才決定放棄,閾值可以/proc/sys/net/ipv4/tcp_synack_retries中修改),客戶端與服務器斷開連接,服務器刪除客戶端在半連接隊列中的SYN分節。
      3. 不論全連接滿沒滿,若半連接隊列已滿:

        1. 不開啓tcp_syncookies的時候,服務器端會丟棄新來的SYN包,而客戶端多次重發SYN包得不到響應而返回超時錯誤(connection time out)。但是當服務器端開啓了tcp_syncookies = 1,那麼SYN半連接隊列就沒有邏輯上的最大值了,並且/proc/sys/net/ipv4/tcp_max_syn_backlog設置的值也會被忽略。
  5. SYN攻擊

    1. 在三次握手過程中,Server發送SYN-ACK之後,收到Client的ACK之前的TCP連接稱爲半連接(half-open connect),此時Server處於SYN_RCVD狀態,當收到ACK後,Server轉入ESTABLISHED狀態。

      1. SYN攻擊就是Client在短時間內僞造大量不存在的IP地址,並向Server不斷地發送SYN包,Server回覆確認包,並等待Client的確認,由於源地址是不存在的,因此,Server需要不斷重發直至超時,這些僞造的SYN包將長時間佔用未連接隊列,導致正常的SYN請求因爲隊列滿而被丟棄,從而引起網絡堵塞甚至系統癱瘓。
      2. SYN攻擊時一種典型的DDOS攻擊,檢測SYN攻擊的方式非常簡單,即當Server上有大量半連接狀態且源IP地址是隨機的,則可以斷定遭到SYN攻擊了,使用如下命令可以讓之現行:#netstat -nap | grep SYN_RECV
    2. SYN攻擊處理

      1. 減少SYN-ACK數據包的重發次數:sysctl -w net.ipv4.tcp_synack_retries=3,sysctl -w net.ipv4.tcp_syn_retries=1
      2. 使用SYN cookie技術:sysctl -w net.ipv4.tcp_syncookies=1
      3. 增加半連接隊列(默認爲1024):sysctl -w net.ipv4.tcp_max_syn_backlog=2048
      4. 限制SYN併發數:iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT --limit 1/s
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章