最近在讀《圖解TCP/IP》,主要是想解決以下困惑: 1.為什麼TCP是面向連線的。 2.為什麼TCP是可靠的(資料包不會亂序,不丟包) 3.為什麼TCP適合傳輸大量資料 4.為什麼TCP傳輸速度比較慢
什麼是TCP/IP協議
TCP協議是OSI(Open System Interconnect)中的第4層傳輸層,IP協議位於OSI中的第3層的網路層,傳統的OSI中有7層,現行的tcp/ip協議族選用了5層架構,把會話層和表示層的功能整合在了應用層。從字面上看TCP/IP是指TCP和IP兩種寫一下,但是IP或ICMP(Internet Control Message Protocol)、TCP和UDP、FTP、HTTP都屬於TCP/IP協議,所以才會說是tcp/ip協議族。 它是為了解決傳輸的穩定性,連續性,和安全性而產生的。
為什麼TCP是面向連線的
其實端到端之間的連線是沒有連線這一說的,TCP之所以說是面向連線的,是因為傳送方和接收方各自維護了一組狀態,以保證雙方的狀態能夠同步,從而看上去是有連線的。TCP協議維護了以下狀態
狀態 | 狀態描述 |
---|---|
CLOSED | 每個連線開始建立之前預設的狀態 |
LISTEN | 一臺裝置(通常是伺服器)等待接受來自客戶端的SYN(Synchronize Sequence Numbers 同步序列號),這時候還沒傳送自己的SYN |
SYN-SENT | 一臺裝置(通常是客戶端)傳送了SYN,並且等待匹配來自其他裝置(通常是服務端)的SYN |
SYN_RECEIVED | 兩端裝置都接收到SYN併傳送了自己的SYN,現在就等待接受ACK(Acknowledgement Number)從而建立連線 |
ESTABLISHED | 一個TCP連線在兩端都已準備就緒,兩端能夠自由的交換資料,直到這個連線被某一端主動關閉 |
CLOSE-WAIT | 裝置收到來自另外一端的關閉連線請求FIN(Finish Number),這時候裝置會接受這個請求並生成對應ACK |
LAST-ACK | 當裝置收到一個FIN併傳送了對應的ACK,然後傳送了自己的FIN並等待對方的ACK回覆 |
FIN-WAIT-1 | 處於這個狀態下的裝置等待自己傳送FIN後對方的ACK應答,或者是等待接收另一端終止連線的請求 |
FIN-WAIT-2 | 處於這個狀態下的裝置已經接收到自己傳送出去的FIN對應的ACK,並等待另一端的FIN請求併傳送自己的ACK |
CLOSING | 裝置收到了另一端的FIN並且傳送了對應的ACK給了對方,但是還沒收到他自己傳送的FIN對應的ACK |
TIME-WAIT | 雙方設定都傳送了FIN,並都接收到了對應的ACK,這時候連線已經完成了,但是連線不會立刻關閉,防止有新的連線複用 |
再放上狀態改變圖,對照表格更好的理解狀態改變
TCP協議有這麼多連線狀態,也就意味著這個協議有多複雜。再加上網路環境的複雜性和不可預知性,因此要是想把TCP協議寫完整不是一朝一夕的事情。為什麼TCP是可靠的(資料包不會亂序,不丟包)
在網路OSI(Open System Interconnection)中,TCP位於第4層,我們的網路資料會放在TCP的Segment(一段網路資料會劃分成許多Segment)的body中,然後Segment會放到IP層的Packet中,再放到資料連結層的Frame中,傳到另一端後,逐層解析各自的協議,然後交給上層處理。 一個TCP Segment的Header裡有如下欄位:
當完成TCP三次握手後,建立連線。當我們有一段資料需要傳輸時,TCP會將資料拆分成一個個Segment,每個Segmen會有一個序號。假設我們有40個Segment需要傳輸:
Seq代表了建立連線後的一個數字,不一定從1開始,雙方都會確立一個ISN(Inital Sequence Number)確立一個初始化序列號,在一個連線週期內(RFC定義為4.55小時)會不斷增加,確保不重複。傳送資料端的Seq會隨著資料包傳輸的長度進行累加。 TCP用以下機制來保證資料的完整性和順序性: 超時重傳機制:當傳送端連續傳送資料包而沒有收到接收端的ack時,會動態計算一個timeout,當在發出資料後,在timeout內沒有收到ack,會啟動重傳機制,以接收方最後一個ack序號,往後傳送資料,若依舊沒有在timeout內有ack回覆會進入timeWait。這個機制重度依賴於timeout的計算演算法。
快速重傳機制:當接收方返回的ack序號連續保持一致時,可以認為資料包丟失,需要啟動快速重傳,以接收方最後一個ack序號,往後傳送資料。
快速重傳機制和超時重傳機制有一個問題無法解決,那就是無法決定只重傳ack所確定的那個包,還是往後的所有包。 比如有5個包,12已收到ack,傳送345後沒有收到ack,這時候是選擇只重傳3呢,還是重傳345呢。這時候就需要SACK出馬了。 **SACK(Selective Acknowledgment)**在TCP頭部中會新增一個SACK欄位用來表示接收到的欄位區間。當接收端將ack和SACK傳送給傳送端後,傳送端就知道哪些資料需要重傳。
為什麼TCP適合傳輸大量資料
因為大量資料會被tcp協議分成一個個segment,然後給每個segment編號,根據SequenceNumber傳送資料,接收端根據SequenceNumber來拼接資料,保證資料的完整性。
為什麼TCP傳輸速度比較慢
首先TCP作為傳輸層協議,不但要對端對端的連線負責,還要對整個網路的連線負責。因此既要保證單一連線的我穩定性,也要保證整個網路的穩定性,當網路情況出現波動時,TCP有能儘量的調整自己的發包速度,避免加重網路堵塞。 因此TCP頭部利用Window欄位和RTT(Round Trip Time,也就是一個資料包從發出去到收到ack的時間)以及RTO(Retransmission TimeOut)來動態改變傳輸的速度。 決定傳輸速度的是cwnd(Congestion Window),單位是MSS(Max Segment Size),乙太網標準MSS是1460位元組,最初慢啟動的cwnd大小是3MSS。 Window : 也就是Sliding Window,滑動視窗,傳送方根據接收方傳過來的這個欄位來決定傳送多少資料。 慢啟動 :當tcp開始傳輸後,每收到一個RTT,就會將cwnd * 2,很明顯這是是一個指數增長的演算法,直到達到threshhold,進入擁塞避免演算法。因此我們下載東西,初期速度都會很小,網路條件好的情況下,幾秒鐘後就能達到最大速度。 擁塞避免 每收到一個ACK時,cwnd = cwnd + 1/cwnd,每過一個RTT時,cwnd = cwnd + 1,進入線性增長 擁塞發生 當RTT > RTO時,tcp認為網路條件差, threshhold = cwnd / 2, cwnd = 1,然後進入慢啟動。 另外一種情況是兩端都支援ECN的TCP連線中,假如在傳輸過程中出現擁塞,路由器會在傳輸的IP包的頭部將ECN欄位置為11,接收端收到這個ECN為11的包後,會將ACK包的ECE(Explict Congestion Notification Echo)欄位置為11,傳送端收到這個Ack後檢查ECE,假如為11,也會進入慢啟動和擁塞避免演算法。
快速恢復 當收到多個重複Ack,cwnd = cwnd /2,sshthresh = cwnd,然後進入擁塞避免