1. 程式人生 > >TCP協議實現原理

TCP協議實現原理

TCP協議是端到端的傳輸控制協議,之所以是“端到端”的協議,是因為”路由“是由IP協議負責的,TCP協議負責為兩個通訊端點提供可靠性保證,這個可靠性不是指一個端點發送的資料,另一個端點肯定能收到(這顯然是不可能的),而是指,資料的可靠投遞或者故障的可靠通知

TCP的可靠性通過以下方式來保證:
1.超時重傳:TCP每傳送出一個報文段後,都會啟動一個定時器,對目的端傳回的確認資訊進行確認計時,超時後便重傳。
2.確認訊號:當TCP收到一個來自TCP的報文段後,便會發送回一個確認訊號。
3.檢驗和:TCP將始終保持首部和資料的檢驗和,如果收到的報文段的檢驗和有差錯,便將其丟棄,希望傳送端超時重傳。
4.重新排序:由於IP資料報的達到可能失序,因此TCP將會資料進行重新排序,以正確的順序交給應用層。

5.丟棄重複:由於IP資料報有可能重複,因此TCP將會丟棄重複的資料。
6.流量控制:TCP連線的兩端都有固定大小的緩衝區空間,TCP接受端只允許對端傳送本端緩衝區能容納的資料。

TCP提供流量控制。在雙方進行互動時,會彼此通知自己目前接收緩衝區最多可以接收的資料量(通告視窗),以此確保傳送方傳送的資料不會溢位接收緩衝區。 
TCP資料包主要包括:
1. SYN包:請求建立連線的資料包
2. ACK包:迴應資料包,表示接收到了對方的某個資料包
3. PSH包:正常資料包
4. FIN包:通訊結束包
5. RST包: 重置連線
導致TCP協議傳送RST包的原因:
   1)SYN 資料段指定的目的埠處沒有接收程序在等待。
   2)TCP協議想放棄一個已經存在的連線。
   3)TCP接收到一個數據段,但是這個資料段所標識的連線不存在。

接收到RST資料段的TCP協議立即將這條連線非正常地斷開,並嚮應用程式報告錯誤。 
6. URG包: 緊急指標

一次完成的TCP通訊包括:建立連線、資料傳輸、關閉連線
建立連線(三次握手):
1.客戶端通過向伺服器端傳送一個SYN來建立一個主動開啟,作為三路握手的一部分。
2.伺服器端應當為一個合法的SYN回送一個SYN/ACK。
3.最後,客戶端再發送一個ACK。這樣就完成了三路握手,並進入了連線建立狀態。
資料傳輸:
1.傳送資料端傳輸PSH資料包
2.接收資料端回覆ACK資料包
關閉連線(四次分手)
1. 一端主動關閉連線。向另一端傳送FIN包。
2. 接收到FIN包的另一端迴應一個ACK資料包。
3. 另一端傳送一個FIN包。

4. 接收到FIN包的原發送方傳送ACK對它進行確認。 

TCP狀態轉換圖:

說明(netstat可檢視狀態):

CLOSED: 初始狀態。

LISTEN: 伺服器端的某個SOCKET處於監聽狀態,可以接受連線了。

SYN_RCVD: 伺服器接受到了SYN報文。

SYN_SENT: 客戶端已傳送SYN報文。

ESTABLISHED:連線已經建立了。

FIN_WAIT_1:當SOCKET在ESTABLISHED狀態時,它想主動關閉連線,向對方傳送了FIN報文,此時該SOCKET即進入到FIN_WAIT_1狀態。而當對方迴應ACK報文後,則進入到FIN_WAIT_2狀態,當然在實際的正常情況下,無論對方何種情況下,都應該馬上回應ACK報文,所以FIN_WAIT_1狀態一般是比較難見到的,而FIN_WAIT_2狀態還有時常常可以用netstat看到。

FIN_WAIT_2:上面已經詳細解釋了這種狀態,實際上FIN_WAIT_2狀態下的SOCKET,表示半連線,也即有一方要求close連線,但另外還告訴對方,我暫時還有點資料需要傳送給你,稍後再關閉連線。

TIME_WAIT: 表示收到了對方的FIN報文,併發送出了ACK報文,就等2個MSL(最大生存時間)值(RFC建議2分鐘,Berkeley傳統使用30s)後即可回到CLOSED可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。
TIME_WAIT狀態存在兩個理由:
1.可靠地實現TCP連線的終止
2.允許老的重複分解在網路中消逝
執行主動關閉的一端都會進入TIME_WAIT狀態,Linux下高併發的Squid伺服器,TCP TIME_WAIT套接字數量經常達到數萬,伺服器很容易被拖死。通過修改Linux核心引數,可以減少伺服器的TIME_WAIT套接字數量。

CLOSING: 這種狀態比較特殊,實際情況中應該是很少見,屬於一種比較罕見的例外狀態。正常情況下,當你傳送FIN報文後,按理來說是應該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。但是CLOSING狀態表示你傳送FIN報文後,並沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麼情況下會出現此種情況呢?其實細想一下,也不難得出結論:那就是如果雙方几乎在同時close一個SOCKET的話,那麼就出現了雙方同時傳送FIN報文的情況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET連線。

CLOSE_WAIT: 這種狀態的含義其實是表示在等待關閉。怎麼理解呢?當對方close一個SOCKET後傳送FIN報文給自己,你係統毫無疑問地會迴應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正需要考慮的事情是察看你是否還有資料傳送給對方,如果沒有的話,那麼你也就可以close這個SOCKET,傳送FIN報文給對方,也即關閉連線。所以你在CLOSE_WAIT狀態下,需要完成的事情是等待你去關閉連線。

LAST_ACK: 被動關閉一方在傳送FIN報文後,最後等待對方的ACK報文。當收到ACK報文後,也即可以進入到CLOSED可用狀態了。

用下面這條語句可以檢視linux伺服器的狀態:
$ netstat -n | awk '/^tcp/ {++Status[$NF]} END {for(name in Status) print name, Status[name]}'
TIME_WAIT 15
CLOSE_WAIT 6
ESTABLISHED 141

TCP傳送資料包過程:

1. 應用程式write資料到傳送緩衝區:如果套介面傳送緩衝區容不下應用程式的所有資料,並且應用程序是阻塞的,應用程序將被掛起,直到所有的資料都拷貝到套介面緩衝區。
2. 
TCP根據MSS(Maxitum Segment Size)指對資料分段傳送。MSS就是TCP資料包每次能夠傳輸的最大資料分段。為了達到最佳的傳輸效能TCP協議在建立連線的時候通常要協商雙方的MSS值,這個值TCP協議在實現的時候往往用MTU(最大傳輸單元,乙太網MTU為1500)值代替(需要減去IP資料包包頭的大小20Bytes和TCP資料段的包頭20Bytes)所以往往MSS為1460。通訊雙方會根據雙方提供的MSS值得最小值確定為這次連線的最大MSS值。