1. 程式人生 > >TCP 三次握手、四次揮手以及安全傳輸

TCP 三次握手、四次揮手以及安全傳輸

TCP協議使用連線管理、流量控制、擁塞控制三大機制來保證可靠性傳輸

三次握手:

開始客戶端處於 close  初始關閉狀態

客戶端:傳送SYN=1(表示請求連線),seq=x(申明自己序號為x)

之後客戶端處於 syn_sent  表示已經發送syn報文

開始伺服器處於listen    監聽狀態,可以接受連線

伺服器:響應SYN=1(表示可以連線),ACK=1 (響應確認連線),ack=x+1(前x收到,下次從x+1開始傳送(下一次訪問的序列號)),seq=y(宣告自己序號為y)

之後伺服器處於syn-rcvd

 表示接受到了syn報文

客戶端:傳送ACK=1(確認連線), ack=y+1, seq=x+1 (前y個已經收到,下一次從y+1開始傳送)

之後客戶端和伺服器都處於 established,established    表示已經連線了

 

為什麼要三次握手,兩次不行嗎?

結論:

為了防止已經失效的連線請求突然再發到服務端,然後服務端一直等待,浪費資源。

模擬場景:

假設只有兩次,客戶端向伺服器傳送請求建立連線的報文,但是這個報文再傳輸過程中在某個網路節點滯留了很長時間,甚至到連線釋放以後才到達服務端,導致其失效,那麼這個失效的報文服務端收到之後誤認為是一個新的請求建立連線的報文然後向客戶端傳送確認,這樣在兩次握手的前提下,一個新的連線就建立成功了,但是客戶端不會理會這樣的失效報文帶來的確認,那麼服務端就會一直在等待,浪費資源。

四次揮手:

以客戶端向伺服器請求關閉為例(當然亦可以是伺服器向客戶端,請求關閉)

客服端開始處於 established狀態  表示已經連線了

客戶端傳送:FIN=1(表示請求斷開),seq=m

之後客戶端處於fin-wait-1  表示等待對方的FIN報文

 

開始伺服器處於 established 表示已連線了

伺服器傳送:ACK=1,ack=m+1 (前m個已經收到,下次m+1) seq=n

之後伺服器處於close-wait等待關閉  客戶端處於fin-wait-2  表示等待對方的FIN報文

 

伺服器傳送:FIN=1 ACK=1 ack=m+1  seq=w  

之後伺服器處於處於last-ack 等待對方ack報文

客戶端傳送ACK=1,ack=w+1,seq=m+1

客戶端處於time-wait 表示收到了對方的報文,等待2msl 即可回到關閉狀態

伺服器處於close狀態

成功的會2msl後客戶端處於close狀態

為什麼需要四次揮手?

結論:

為了保證資料的完全傳輸。

解釋:

TCP是全雙工模式,是單方面斷開連線,只有雙方都斷開才會釋放本次連線。

那麼當客戶端請求斷開連線,伺服器收到立馬回覆確認,只是表明伺服器知道客戶端沒有資料傳送了,但是伺服器本身還是可以繼續發資料接收資料的。此時,客戶端會處於fin-wait半關閉狀態,也就意味著它不能傳送資料但是依舊能接收資料。只有當服務端傳送完資料之後也請求斷開連線,客戶端收到立馬回覆確認之後伺服器才能斷開,而這時,客戶端等待2msl之後也斷開。兩端都關閉連線,全雙工TCP連線真正釋放。

注意:實際上也很有可能是三次揮手。因為如果服務端沒有資料要傳送的話就可以在客戶端請求斷開連線之後,服務端傳送確認同時也請求斷開連線,FIN和ACK同時傳送,這時客戶端接收並關閉連線,兩端都關閉連線,本次連線釋放。

 

為什麼需要2msl?

兩個理由:1)保證A傳送的最後一個ACK報文段能夠到達B。2)防止“已失效的連線請求報文段”出現在本連線中。

1)當A傳送ACK到B(1msl),如果B沒有收到會從發FIN(1msl)。故2msl內A沒有收到FIN表示B已經收到ACK,反之沒有收到,需要重發

2)A在傳送完最後一個ACK報文段後,再經過2MSL,就可以使本連線持續的時間內所產生的所有報文段都從網路中消失,使下一個新的連線中不會出現這種舊的連線請求報文段。

 

 

3.TCP擁塞控制

作用:防止過多的資料諸如網路,使網路負載過大,讓網路能夠承載現在的負荷,實現供求平衡

控制方法:

接受視窗RWin -- 根據接收方自身讀取速度以及接收快取的大小來設定(在緩衝區存在)

擁塞視窗CWin -- 傳送端根據自己估計的網路擁塞成都設定的視窗值(假想的實際上不存在)

設定原則:

網路沒有擁塞,擁塞視窗設定大一些保證更多的資料在網路條件好的時候發出去,

網路出現擁塞,擁塞視窗就小一些,以便當前網路上注入的資料少一些,

從而保證傳輸。

傳送視窗的上限值 = Min[ CWin,RWin ]

傳送端傳送多少資料以接收視窗和擁塞視窗中較小的為基準

當 RWin < CWin 時,是接收端的接收能力限制傳送視窗的最大值。

當 RWin > CWin時,則是網路的擁塞情況限制傳送視窗的最大值。

擁塞控制具體實現:

 

1.慢啟動:指數增長,對擁塞視窗進行增加,保證儘可能的傳輸資料,每次收到一個確認就將視窗增大,增加到閾值就進入擁塞避免。

2.擁塞避免:使用加法增大演算法,每經過一個往返時間RTT才會將擁塞視窗+1而不是加倍,當增加到當前網路能夠承受的最大值的時候就要進行乘法減小演算法,將閾值變成當前最大視窗(網路能夠負載的最大能力)的一半,然後再將擁塞視窗減到1,重新進行慢啟動,擁塞避免的過程

3.快重傳:收到三個重複確認,不用等到重傳計時器到期就直接傳送丟失的報文

4.快恢復:一旦發生丟失報文的情況,就悲觀的認為當前網路已經到了負載能力最大的地步,就直接開始執行擁塞避免,而不用等到慢啟動到達門限值之後再啟動擁塞避免。

4.TCP流量控制

簡述:就是給返回報文欄位中新增一個流量視窗大小的欄位,從而告知傳送方,下一次傳送資料的時候需要根據流量視窗的大小來發送資料,從而防止傳送方傳送過快而接收方根本讀不過來。

實現流量控制的方法:

序號,確認,超時重傳,滑動視窗等可靠性傳輸機制

滑動視窗:傳送方的緩衝區一部分是視窗,那麼窗口裡放的資料是允許傳送的資料和已傳送但未確認的資料,一旦傳送並確認,視窗就會向傳送方方向挪動,以便傳送下一個資料。

接收方的緩衝區同樣有一部分是視窗,存放的是允許接收和已接收但未確認的資料,一旦接收並確認,接收視窗就會向傳送方方向(相反於自身方向)滑動。

傳送視窗滿就停止:當接收方一直不確認,傳送方一直髮送允許傳送的資料,傳送視窗中就全部都是已傳送但未確認的資料,傳送視窗就會變滿,此時就停止傳送。

接收方接收能力弱:接收方來不及接收資料就會通知傳送方縮小視窗。

亂序報文解決:當接收方接收到亂序的報文會先將它存在視窗中並啟動超時重傳,等待發送方傳送缺少的資料,最後再進行重組。

丟失報文解決:傳送過程中報文丟失,接收方無法確認,那傳送方就會一直髮送,直到傳送方視窗變滿就會重新把丟失的報文發一遍。

 

上述情況產生的問題:

RWin=0是因為接收方緩衝區滿了,就是讀取比較慢,等接收方讀取後,接收方緩衝區就空下來,

這時候接收方向傳送方傳送RWin=400,但是很不巧這個報文段丟了,傳送方一直在等待接收方說它空下來的訊息,但是接收方不知道自己之前傳送的丟了,所以也一直在等待發送方傳送資料?

尷尬。。。怎麼辦?

別擔心,持續計時器!!!!

只要TCP連線的一方收到對方的零視窗通知,就啟動持續計時器來預防發生上述問題。

工作原理:若持續計時器設定的時間到期就傳送一個視窗探測報文段攜帶一個位元組的資料。

就是說接收方給傳送方說:你等下再發,我這視窗滿了,我要是視窗空下來我就給你說。

傳送方就一直等著也不是辦法,所以就主動給接收方打個電話問一下:你看現在行不行?

接收方要是視窗空下

來就給傳送方傳送確認報文段也就是當前的視窗值:

如果確認報文段中視窗欄位值為0則傳送方就再設定持續計時器,

不是0就繼續進行正常的傳送和接收

當然如果老是0,傳送方就不自己打電話問了,就設定一個週期,週期性傳送探測

5.TCP和UDP區別

 

TCP

UDP

連線

面向連線 只有在確認通訊對端存在時才會傳送資料, 從而可以控制通訊流量的浪費

面向無連線

傳輸

面向位元組流 把TCP資料看成一連串無結構的位元組流

面向報文 沒有擁塞控制, 網路擁塞不會導致源主機發送速率降低,對實時應用有用

可靠性

可靠性傳輸,保證正確性和資料順序 無差錯,不丟失,不重複,按序到達 (三大機制:流量控制,三握四揮,擁塞控制)

不可靠,不保證正確性和資料順序 盡最大努力進行交付,不保證可靠性交付

連線

點對點

支援一對一,一對多,多對多互動通訊

開銷

首部開銷:20位元組

首部開銷:8位元組

邏輯通 信通道

全雙工的可靠通道

不可靠通道

 

6.如何實現UDP可靠通訊?

為什麼要可靠?TCP在網路環境下要麼無法提供正常的通訊質量,要麼成本過高,所以我們在保證通訊的時延和質量的條件下儘量降低成本從而改造UDP

思想:儘量讓UDP往TCP的特性靠,同時也具有自己傳輸快,傳輸量大的特點。

做重傳機制(分為兩種):

傳送者發起重傳:(接收者如果收到報文要向傳送者傳送確認)

對於傳送者發起的方式,一般情況下接收者會發送一個訊息包的確認。傳送者維護一個

計時器並重傳那些在某個確定的時間段裡沒有收到確認的訊息包。這一型別的協議容易

引起傳送者溢位,因為要確認每一個傳送的訊息包。這種溢位現象被稱為傳送者(或者

ACK)內爆。

接受者發起重傳:(接收者通過序號檢查有沒有報文丟失,從而讓傳送者重傳)

對於接收者發起的方式,通訊雙方的接收者負責錯誤檢測。在這個方式裡,序列號被用

於檢測訊息包丟失。當檢測到訊息包丟失,接收者請求傳送者重傳訊息包。採用這種方

法,如果訊息包沒有到達任何一個接收者,傳送者容易因NACK溢位。這會引起傳送者

的負載過高和過多的重傳。這種現像被稱為NACK內爆。Ramakrishnan et al.提出可以

使用定時器來限制訊息包重傳,從而避免NACK內爆。

RUDP

三角平衡關係:實時通訊中存在的三角平衡關係

 

盡力可靠:通訊的接收方要求傳送方的資料儘量完整到達,但業務本身的資料是可以允許缺失的。例如:音視訊資料、冪等性狀態資料。

無序可靠:通訊的接收方要求傳送方的資料必須完整到達,但可以不管到達先後順序。例如:檔案傳輸、白板書寫、圖形實時繪製資料、日誌型追加資料等。

有序可靠:通訊接收方要求傳送方的資料必須按順序完整到達。

RUDP根據這三類需求以及制約關係來確定自己的通訊模型和機制,找到通訊的平衡點。

也是根據TCP的思想,使用重傳,擁塞控制,流量視窗,但具體實現完了再學習。。。。。

7.TCP長連線,短連線,保活,心跳包

TCP短連線:

客戶端想伺服器發起連線請求,伺服器接收到請求,然後雙方建立連線。

客戶端向伺服器傳送訊息,伺服器迴應客戶端,然後讀寫一次就完成了,這時候雙方任何一個都可以發起close操作。不過一般都是客戶端先發起close操作,原因是一般伺服器除特殊情況外不會回覆完客戶端之後立即關閉連線。短連線一般只會在客戶端/伺服器之間傳遞一次讀寫操作

優點:管理起來比較簡單,存在的連線都是有用的連線,不需要額外的控制手段

TCP長連線:

客戶端向伺服器發起連線請求,伺服器接收請求,雙方建立連線。客戶端與伺服器完成一次讀寫之後,他們之間的連線並不會主動關閉,後續的讀寫操作會繼續使用這個連線。

TCP保活:

為伺服器應用提供,伺服器應用需要知道客戶主機是否崩潰。如果客戶端已經消失,伺服器就保留了一個半開放的連線,保活功能就是試圖在伺服器端檢測到這種半開放的連線

如果一個給定的連線在兩個小時內沒有任何動作,則伺服器就向客戶傳送一個探測報文段,客戶主機必須處於以下四個狀態之一:

1.客戶主機依然正常執行,並從伺服器可達。客戶的TCP響應正常,而伺服器也知道對方是正常的,伺服器在兩小時後將保活定時器復位。

2.客戶主機已經崩潰,並且關閉或者正在重新啟動。在任何一種情況下,客戶的TCP都沒有響應。服務端將不能收到對探測的響應,並在75秒後超時。伺服器總共傳送10個這樣的探測 ,每個間隔75秒。如果伺服器沒有收到一個響應,它就認為客戶主機已經關閉並終止連線。

3.客戶主機崩潰並已經重新啟動。伺服器將收到一個對其保活探測的響應,這個響應是一個復位,使得伺服器終止這個連線。

4.客戶機正常執行,但是伺服器不可達,這種情況與2類似,TCP能發現的就是沒有收到探查的響應。

長連線和短連線的產生在於客戶端和伺服器採取的關閉策略

心跳包:

每隔一段時間(TCP預設2小時)固定發一次包(內容一般是很小的包或者只包含包頭)給客戶端,從而告訴伺服器客戶端還活著。只要send或者recv一下,結果為零,則為掉線

三、TCP的11種狀態轉換

CLOSED:起始點,不在連線狀態。可以主動開啟連線,或者等待對端的連線。

-->收到“被動開啟”報文,進入LISTEN狀態。

-->收到“主動開啟”報文,進入SYN_SENT狀態。

-->收到任何報文段,傳送RST報文段。

-->收到其它任何報文段,發出差錯報文。

LISTEN:被動開啟,TCP正在等待對端的連線請求。

-->收到“傳送資料”報文,傳送SYN報文段,進入SYN_SENT狀態。

-->收到任何SYN報文段,傳送SYN+ACK報文段,進入SYN_RECEIVED狀態。

-->收到任何其它報文段或者報文,傳送差錯報文。

SYN_SENT:主動開啟,傳送完一個連線請求後等待回覆。

-->超時,進入CLOSED狀態。

-->收到SYN報文段,傳送SYN+ACK報文段,進入SYN_RECEIVED狀態。

-->收到SYN+ACK報文段,傳送ACK報文段,進入ESTABLISHED狀態。

-->收到任何其它報文段或者報文,傳送差錯報文。

SYN_RECEIVED:被動開啟,接受連線請求以後進行確認同時也向對端傳送連線請求傳送,等待對方的回覆。

-->超時,傳送RST報文段,進入CLOSED狀態。

-->收到ACK報文段,進入ESTABLISHED狀態。

-->收到"關閉"報文,傳送FIN報文段,進入FIN_WAIT_1狀態。

-->收到RST報文段,進入LISTEN狀態。

-->收到任何其它報文段或者報文,傳送差錯報文。

ESTABLISHED:三次握手完畢,TCP連線建立完成,可以傳輸資料。

-->收到FIN報文段,進入CLOSED_WAIT狀態。

-->收到“關閉”報文,傳送FIN報文段,進入FIN_WAIT_1狀態。

-->收到RST或SYN報文段,發出差錯報文。

-->收到資料或ACK報文段,呼叫輸入模組。

-->收到“傳送”報文,呼叫輸出模組。

FIN_WAIT_1:四次揮手開始,主動關閉,傳送斷開連線請求,等待對端確認。

-->收到FIN報文段,傳送ACK報文段,進入CLOSING狀態(同時關閉)。

-->收到FIN+ACK報文段,傳送ACK報文段,進入FIN_WAIT狀態(?)。

-->收到ACK報文段,進入FIN_WAIT_2狀態。

-->收到任何其它報文段或者報文,傳送差錯報文。

FIN_WAIT_2:接收對方確認,但未接受對方的斷開連線請求。

-->收到FIN報文段,傳送ACK報文段,進入TIME_WAIT狀態。

CLOSING:主動關閉的一方本希望收到對方的ACK卻收到了對方的斷開連線請求。

-->收到ACK報文段,進入TIME_WAIT狀態。

-->收到任何其它報文段或者報文,傳送差錯報文。

TIME_WAIT:對方確認後發起斷開連線請求,需要等待2MSL保證正常關閉。

-->超時,進入CLOSED狀態。

-->收到任何其它報文段或者報文,傳送差錯報文。

CLOSE_WAIT:被動關閉,確認對端的連線終止請求,但是未向對端傳送連線終止請求(可能資料沒傳完)。

-->收到"關閉"報文,傳送FIN報文段,進入LAST_ACK狀態。

-->收到任何其它報文段或者報文,傳送差錯報文。

LAST_ACK:資料傳完,向對端發起斷開連線請求後等待確認。

-->收到ACK報文段,進入CLOSED狀態。

-->收到任何其它報文段或者報文,傳送差錯報文。

CLOSED:終點,不在連線狀態。可以主動開啟連線,或者等待對端的連線。

 

(1)TIME_WAIT狀態(假設客戶端主動,伺服器端被動)

從狀態圖中我們可以發現,執行主動關閉的那端(最終重傳ACK的那端)會經歷這個狀態。這個狀態停留是的時間是2MSL(MSL:最長報文段生存時間,1~4分鐘)。

TIME_WAIT狀態的作用:

1、可靠地實現TCP的連線終止。

在終止TCP連線時有4個報文需要交換,其中最後一個ACK報文是由客戶端發往伺服器。假設這個ACK報文在網路中被丟棄了,那麼伺服器端收不到這個確認ACK,伺服器端會向客戶端再次傳送FIN。這就是為什麼TIME_WAIT狀態持續2倍的最長報文段生存時間:1MSL時間留給最後的ACK確認報文段到達伺服器端,1MSL時間留給伺服器端再次傳送的FIN(這一段摘抄自《unix系統程式設計手冊》P1046,我不明白,超過2MSL連線就超時關閉了,再次傳送FIN後,即客戶端剛收到,2MSL時間也就到了,連線就關閉了,再次傳送FIN的意義何在?,希望有知道的同學告訴我)。

 

2、確保老的重複的報文段在網路中過期失效,這樣建立新的連線時將不再接受它們。

TCP協議採用的是出錯重傳,也就是會生成重複的報文,並且根據路由器的選擇,這些重複的報文可能在連線終止後才到達,如果客戶端/伺服器端收到這個老的報文會把它誤認為一個同一連線的新的報文,然後對這個報文進行處理,這樣就會出現錯誤。從狀態轉換圖我們可以看到從TIME_WAIT到連線終止,中間有2MSL,這個時間足以讓老的重複的報文段過期失效。

 

TIME_WAIT會出現在伺服器嗎?

會的,TIME_WATI存在首先執行主動關閉的那端,比如爬蟲伺服器他本身其實就是客戶端,完成爬取任務後,執行關閉,那麼所有的TCP連線都會處於TIME_WAIT狀態。

 

TIME_WAIT的危害:Linux分配給一個使用者的檔案控制代碼是有限的,如果系統中存在大量的TIME_WAIT狀態,一旦達到控制代碼數上限,新的請求就無法被處理了,而且大量TIME_WAIT連線佔用資源影響效能。