TCP(Transportation Control Protocol)協議與IP協議是一同產生的。事實上,二者最初是一個協議,後來才被分拆成網絡層的IP和傳輸層的TCP。咱們已經在UDP協議中介紹過,UDP協議是IP協議在傳輸層的「傀儡」,用來實現數據包形式的通訊。而TCP協議則實現了「流」形式的通訊。html
TCP的內容很是豐富。我不能在一篇文章中將TCP講完。這一篇主要介紹TCP協議的下面幾個方面:算法
1. 「流」通訊的意義與實現方式緩存
2. 如何實現可靠傳輸微信
3. 使用滑窗提升效率網絡
TCP協議是傳輸層協議,實現的是端口到端口(port)的通訊。更進一步,TCP協議虛擬了文本流(byte stream)的通訊。在Linux文本流中咱們談到,計算機數據的本質是有序的0/1序列 (若是以byte爲單位,就叫作文本流)。計算機的功能就是儲存和處理文本流。CPU + memory + 存儲設備實現了文本流在同一臺計算機內部的加工處理。經過一些IO,好比屏幕和鍵盤,文本流實現了人機交互。而進一步,若是網絡通訊可在不一樣計算機之間進行文本流的交互,那麼咱們就和整個計算機系統的數據處理方式實現了對接。學習
IP協議(參考協議森林03, 05)和UDP協議採用的是數據包的方式傳送,後發出的數據包可能早到,咱們並不能保證數據到達的次序。TCP協議確保了數據到達的順序與文本流順序相符。當計算機從TCP協議的接口讀取數據時,這些數據已是排列好順序的「流」了。好比咱們有一個大文件要從本地主機發送到遠程主機,若是是按照「流」接收到的話,咱們能夠一邊接收,一邊將文本流存入文件系統。這樣,等到「流」接收完了,硬盤寫入操做也已經完成。若是採起UDP的傳輸方式,咱們須要等到全部的數據到達後,進行排序,才能組裝成大的文件。這種狀況下,咱們不得不使用大量的計算機資源來存儲已經到達的數據,直到全部數據都達到了,才能開始處理。3d
「流」的要點是次序(order),然而實現這一點並不簡單。TCP協議是基於IP協議的,因此最終數據傳送仍是以IP數據包爲單位進行的。若是一個文本流很長的話,咱們不可能將整個文本流放入到一個IP數據包中,那樣有可能會超過MTU。因此,TCP協議封裝到IP包的不是整個文本流,而是TCP協議所規定的片斷(segment)。與以前的一個IP或者UDP數據包相似,一個TCP片斷一樣分爲頭部(header)和數據(payload)兩部分 (「片斷」這個名字更可能是起提醒做用:嘿,這裏並非完整的文本流)。整個文本流按照次序被分紅小段,而每一段被放入TCP片斷的數據部分。一個TCP片斷封裝成的IP包不超過整個IP接力路徑上的最小MTU,從而避免使人痛苦的碎片化(fragmentation)。視頻
(給文本流分段是在發送主機完成的,而碎片化是在網絡中的路由器完成的。路由器要處理許多路的通訊,因此至關繁忙。文本流提早在發送主機分好段,能夠避免在路由器上執行碎片化,可大大減少網絡負擔)htm
片斷與編號blog
TCP片斷的頭部(header)會存有該片斷的序號(sequence number)。這樣,接收的計算機就能夠知道接收到的片斷在原文本流中的順序了,也能夠知道本身下一步須要接收哪一個片斷以造成流。好比已經接收到了片斷1,片斷2,片斷3,那麼接收主機就開始期待片斷4。若是接收到不符合順序的數據包(好比片斷8),接收方的TCP模塊能夠拒絕接收,從而保證呈現給接收主機的信息是符合次序的「流」。
片斷編號這個初步的想法並不能解決咱們全部的問題。IP協議是不可靠的,因此IP數據包可能在傳輸過程當中發生錯誤或者丟失。而IP傳輸是"Best Effort" 式的,若是發生異常狀況,咱們的IP數據包就會被輕易的丟棄掉。另外一方面,若是亂序(out-of-order)片斷到達,根據咱們上面說的,接收主機不會接收。這樣,錯誤片斷、丟失片斷和被拒片斷的聯手破壞之下,接收主機只可能收到一個充滿「漏洞」的文本流。
請補上漏洞
TCP的補救方法是,在每收到一個正確的、符合次序的片斷以後,就向發送方(也就是鏈接的另外一段)發送一個特殊的TCP片斷,用來知會(ACK,acknowledge)發送方:我已經收到那個片斷了。這個特殊的TCP片斷叫作ACK回覆。若是一個片斷序號爲L,對應ACK回覆有回覆號L+1,也就是接收方期待接收的下一個發送片斷的序號。若是發送方在必定時間等待以後,仍是沒有收到ACK回覆,那麼它推斷以前發送的片斷必定發生了異常。發送方會重複發送(retransmit)那個出現異常的片斷,等待ACK回覆,若是尚未收到,那麼再重複發送原片斷... 直到收到該片斷對應的ACK回覆(回覆號爲L+1的ACK)。
終於收到ACK的發送主機
當發送方收到ACK回覆時,它看到裏面的回覆號爲L+1,也就是發送方下一個應該發送的TCP片斷序號。發送方推斷出以前的片斷已經被正確的接收,隨後發出L+1號片斷。ACK回覆也有可能丟失。對於發送方來講,這和接收方拒絕發送ACK回覆是同樣的。發送方會重複發送,而接收方接收到已知會過的片斷,推斷出ACK回覆丟失,會從新發送ACK回覆。
經過ACK回覆和從新發送機制,TCP協議將片斷傳輸變得可靠。儘管底盤是不可靠的IP協議,但TCP協議以一種「不放棄的精神」,不斷嘗試,最終成功。(技術也能夠很勵志)
面對「挫折」,TCP協議的態度: never give up
TCP協議和UDP協議走了兩個極端。TCP協議複雜但可靠,UDP協議輕便但不可靠。在處理異常的時候,TCP極端負責,而UDP一副無所謂的樣子。咱們能夠順便「黑」一下UDP協議:
一樣面對「挫折」,UDP的態度: who cares...
上面的工做方式中,發送方保持發送->等待ACK->發送->等待ACK...的單線工做方式,這樣的工做方式叫作stop-and-wait。stop-and-wait雖然實現了TCP通訊的可靠性,但同時犧牲了網絡通訊的效率。在等待ACK的時間段內,咱們的網絡都處於閒置(idle)狀態。咱們但願有一種方式,能夠同時發送出多個片斷。然而若是同時發出多個片斷,那麼因爲IP包傳送是無次序的,有可能會生成亂序片斷(out-of-order),也就是後發出的片斷先到達。在stop-and-wait的工做方式下,亂序片斷徹底被拒絕,這也很不效率。畢竟,亂序片斷只是提早到達的片斷。咱們能夠在緩存中先存放它,等到它以前的片斷補充完畢,再將它綴在後面。然而,若是一個亂序片斷實在是太過提早(太「亂」了),該片斷將長時間佔用緩存。咱們須要一種折中的方法來解決該問題:利用緩存保留一些「不那麼亂」的片斷,指望能在段時間內補充上以前的片斷(暫不處理,但發送相應的ACK);對於「亂」的比較厲害的片斷,則將它們拒絕(不處理,也不發送對應的ACK)。
總有那麼幾個「出格」片斷
滑窗(sliding window)被同時應用於接收方和發送方,以解決以上問題。發送方和接收方各有一個滑窗。當片斷位於滑窗中時,表示TCP正在處理該片斷。滑窗中能夠有多個片斷,也就是能夠同時處理多個片斷。滑窗越大,越大的滑窗同時處理的片斷數目越多(固然,計算機也必須分配出更多的緩存供滑窗使用)。
同時處理多個片斷
咱們假設一個能夠容納三個片斷的滑窗,並假設片斷從左向右排列。對於發送方來講,滑窗的左側爲已發送並已ACK過的片斷序列,滑窗右側是還沒有發送的片斷序列。滑窗中的片斷(好比片斷5,6,7)被髮送出去,並等待相應的ACK。若是收到片斷5的ACK,滑窗將向右移動。這樣,新的片斷從右側進入滑窗內,被髮送出去,並進入等待狀態。在接收到片斷5的ACK以前,滑窗不會移動,即便已經收到了片斷6和7的ACK。這樣,就保證了滑窗左側的序列是已經發送的、接收到ACK的、符合順序的片斷序列。
對於接收方來講,滑窗的左側是已經正確收到並ACK回覆過的片斷(好比片斷1,2,3,4),也就是正確接收到的文本流。滑窗中是指望接收的片斷(好比片斷5, 6, 7)。一樣,若是片斷6,7先到達,那麼滑窗不會移動。若是片斷5先到達,那麼滑窗會向右移動,以等待接收新的片斷。若是出現滑窗以外的片斷,好比片斷9,那麼滑窗將拒絕接收。
下面一個視頻中,嘗試模擬可容納三個片斷的滑窗(固定大小)的工做過程。
可點下面連接: http://v.youku.com/v_show/id_XNDg1NDUyMDUy.html
上面的視頻是用Python和matplotlib包製做的。藍色點表示片斷,紅色點表示ACK。爲了說明亂序片斷,我故意讓片斷和ACK的速度從兩個值中隨機選擇。
能夠看到,隨着滑窗的滑動,愈來愈多的片斷被正確的傳送。利用滑窗,咱們必定程度上實現了對亂序數據的緩存。可是,過於亂序的數據依然會被拒絕。咱們以前說的stop-and-wait的工做方式,至關於發送方和接收方的滑窗都只能容納一個片斷。
咱們將在之後看到,TCP協議有實時調整滑窗大小的算法,以實現最優效率。
TCP協議和UDP協議走了兩個極端。TCP協議複雜但可靠,UDP協議輕便但不可靠。在處理異常的時候,TCP極端負責,而UDP一副無所謂的樣子。在TCP中,分段和編號實現了次序;ACK和從新發送實現了可靠性;sliding window則讓上面的機制更加有效率的運行。Never give up,這就是TCP協議的態度。
做者:Vamei 出處:http://www.cnblogs.com/vamei
歡迎添加我的微信號:Like若所思。
歡迎關注個人公衆號,不只爲你推薦最新的博文,還有更多驚喜和資源在等着你!一塊兒學習共同進步!