tcp 基本工作

TCP由RFC793、RFC1122、RFC1323、RFC2001、RFC2018以及RFC2581定義。


(1) TCP概述
a. TCP提供的是面向連接的全雙工服務。
TCP所有的數據會匹配到由源地址,目的地址,源端口,目的端口構成的一個TCP連接之上。TCP連接是一種需要建立的資源,可以通過之後會講到的握手機制來完成。UDP是一種基於盡力而爲機制的協議,不存在UDP連接資源的建立,資源的處理往往由應用層協議代勞了。


b. TCP是提供的可靠服務。
TCP有確認機制來保證數據包的可靠到達,
TCP有CRC校驗機制來保證數據包的無差錯性,UDP的CRC是可選的,
TCP會重新排序亂序的數據包和丟棄重複的數據,
TCP能夠提供流量控制機制,使用滑動窗口算法,
TCP能提供擁塞控制與恢復機制,存在多種TCP擁塞控制模型,
TCP能協商發送的數據報文長度。


TCP報頭。
    0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |           |U|A|P|R|S|F|                               |
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
   |       |           |G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                            TCP Header Format


對於TCP頭的標記位,SYN標記只在三次握手(或四次握手)的時候的被置位,ACK標記會在握手之後所有的TCP報文中被置位。當然也有一些特殊情況,比如有些情況下RST報文不會置位ACK。
這些規則也許在配置複雜的ACL中有用。


(2) TCP協議棧的狀態機 (摘自RFC793)
a. TCP連接的建立。TCP連接的建立有主動打開,被動打開以及同時打開三種情況。
三次握手比較清楚,要強調的是ISN,就是初始序列號的選擇問題,序列號是32位的,針對不同的OS,初始序列號的選擇往往也是有規律的。


TCP傳輸的最大報文長度也是在三次握手中協商的。具體說是在也僅在SYN報文中協商的。MSS = MTU - ip_header_len - tcp_header_len。MSS這裏也是爲了防止分片,提高網絡帶寬利用率。


TCP三次握手中,最後一個報文ACK,不需要再有額外的確認機制,如果這個ACK在網絡中丟棄了,TCP協議棧也有其他的機制來處理。
除了三次握手,還有一種很特殊的應用情況,就是TCP兩端同時打開的情況(發送syn),這種情況沒有描述在上面的狀態機中。


舉例子來說,A通過源端口7777發起到B的目的端口8888的連接的同時,B也通過源端口8888發起對A的目的端口7777的TCP連接。


b. TCP連接的關閉
TCP連接的關閉也有主動關閉,被動關閉和同時關閉三種情況,這三種情況在上面的TCP狀態機中都有描述。
TCP連接的關閉需要報文四次交互,因爲TCP是一個全雙工的服務,所以每個方向的連接都關閉後,TCP的連接纔是完整的拆除。
狀態機中,主動關閉和同時關閉最後都會進入到一個TIME_WAITE狀態。針對TCP主動關閉的最後一個報文應該是ACK,確認對端的FIN報文。這個狀態的概念是該TCP連接的資源並沒有完全釋放,因爲還要確保最後一個ACK報文能夠無誤的到達對端,確認對端的FIN,否則就仍然要重傳ACK。
這個等待的過程(或者資源沒有完全釋放的過程)需要等待2MSL時間(考慮報文一次往返)。MSL是最大報文生存時間,RFC793中爲2分鐘,根據不同的TCP實現,一般是30s或者1分鐘。


所以在TIME_WAITE狀態內,該TCP連接所使用的端口和連接資源,不能被繼續使用。但是很多TCP實現並沒有這個限制,只要新的TCP連接所使用的ISN大於TIME_WAITE狀態TCP連接所使用的最後序號即可。實現中往往使用
new ISN = latest ISN in time_waite + 128000
IP報文的最大生存時間是TTL值,TCP報文的最大生存時間是MSL,二層上沒有報文最大生存時間的概念,存在風暴的可能。


(3) TCP的滑動窗和定時器
a. TCP的報文確認機制。
TCP使用的是滑動窗口機制來發送數據流,所以TCP協議允許連續發送多個TCP分組而不等待對端的確認。所以發送的分組數據和確認不是一對一的關係。
TCP中,對數據的確認往往是延遲的,一般情況是兩個TCP數據對應一個確認,在時延定時器沒有溢出的情況下。如果時延定時器溢出了,那麼自然也會發送確認報文。
但是,針對存在交互大量微小報文的TCP應用,過於頻繁的確認會導致網絡利用率的低效,所以TCP支持一種Nagle算法。


b. 延時定時器
當TCP收到報文時候,啓動延時定時器,比如200ms。


c. Nagle算法
TCP連接上只能存在一個未被確認的微小報文(41字節的TCP報文),在該確認到達前,TCP僅僅收集微小報文,當確認到達後,以一個分組的形式發出去。
當然,某些應用需要關閉Nagle算法。


d. 滑動窗口機制
窗口合攏(左移):在收到對端數據後,自己確認了數據的正確性,這些數據會被存儲到緩衝區,等待應用程序獲取。但這時候因爲已經確認了數據的正確性,需要向對方發送確認響應ACK,又因爲這些數據還沒有被應用進程取走,這時候便需要進行窗口合攏,緩衝區的窗口左邊緣向右滑動。注意響應的ACK序號是對方發送數據包的序號,一個對方發送的序號,可能因爲窗口張開會被響應(ACK)多次。


窗口張開(右移):窗口收縮後,應用進程一旦從緩衝區中取出數據,TCP的滑動窗口需要進行擴張,這時候窗口的右邊緣向右擴張,實際上窗口這是一個環形緩衝區,窗口的右邊緣擴張會使用原來被應用進程取走內容的緩衝區。在窗口進行擴張後,需要使用ACK通知對端,這時候ACK的序號依然是上次確認收到包的序號。


窗口收縮,窗口的右邊緣向左滑動,稱爲窗口收縮,Host Requirement RFC強烈建議不要這樣做,但TCP必須能夠在某一端產生這種情況時進行處理。


e. 重傳定時器
目的是爲了獲得對端的確認報文。如果多次重傳仍然沒有獲得確認,則會發送復位報文RST。


這裏我們再來看一下TCP的三次握手。
A(發起端) ---> syn ---> B(服務器)
A(發起端) <--- syn/ack <--- B(服務器)
A(發起端) ---> ack    ?   B(服務器)


如果TCP客戶端A的最後一個ACK丟失了,TCP服務器B沒有收到,會是一種什麼情況?
這個時候A已經進入到了Establish狀態,然而B還只是Syn_Recev狀態,所以服務器會重傳syn/ack報文,只到連接的最終建立。但是客戶端A已經到建立狀態了,所以A是有可能發送TCP數據給服務器B的。
所以TCP的兩端,最終狀態機是有可能不一致的。


後面會詳細講述重傳和擁塞控制機制。


f. 堅持定時器
由於TCP沒有對ACK的確認機制,所以當接收端窗口從0恢復到一定值的時候,如果接收端發給發送端的ACK報文(標識窗口大小)丟失了,發送端就永遠不知道接收端的窗口恢復情況了。
所以發送端會定時發送帶一個字節的ACK給接收端,查看接收端的確認報文中的窗口信息。


g. 保活定時器
由於物理原因,處於IDLE狀態的TCP連接一端崩潰的時候,TCP有保活機制來判斷對端是否仍然工作。這個設計存在爭議,也許應用層應該實現該功能。RFC1122中有描述,保活定時器默認是關閉的。下面截取了一些RFC描述。
Implementors MAY include "keep-alives" in their TCP implementations, although this practice is not universally accepted.  If keep-alives are included, the application MUST be able to turn them on or off for each TCP connection, and they MUST default to off.


(4) TCP擁塞控制算法:慢啓動、擁塞避免、快速重傳和快速恢復
針對擁塞控制,主要有四種模型,即TCP TAHOE,TCP RENO,TCP NEWRENO和TCP SACK。TCP TAHOE模型是最早的TCP協議之一,它由Jacobson提出。


Jacobson觀察到,TCP報文段(TCP Segment)丟失有兩種原因,其一是報文段損壞,其二是網絡阻塞,而當時的網絡主要是有線網絡,不易出現報文段損壞的情況,網絡阻塞爲報文段丟失的主要原因。針對這種情況,TCP TAHOE對原有協議進行了性能優化,其特點是,在正常情況下,通過重傳計時器是否超時和是否收到重複確認信息(dupack)這兩種丟包監測機制來判斷是否發生丟包,以啓動擁塞控制策略;在擁塞控制的情況下,採用慢速啓動(Slow Start)算法和“擁塞避免”(Congestion Avoidance)算法來控制傳輸速率。 1990年出現的TCP Reno版本增加了“快速重傳 ”(Fast Retransmit)、“快速恢復”(Fast Recovery)算法,避免了網絡擁塞不嚴重時採用“慢啓動”算法而造成過度減小發送窗口尺寸的現象,這樣TCP的擁塞控制就主要由這4個核心算法組成。 
b. 慢啓動和擁塞避免算法
慢啓動算法的目的是爲了保證TCP發送方發送分組的速率應該匹配收到該分組確認報文的速率,這樣的設計能夠應用於低速鏈路的廣域網應用。爲了實現慢啓動機制,爲TCP連接增加了一個新的窗口,擁塞窗口cwnd,該窗口初始化爲一個報文段(非一個字節,而是一個TCP最大傳輸報文段大小,MSS)。這樣一個方向上的TCP連接有兩個窗口,一個是接收窗口用於接收方的流量控制,一個是擁塞窗口用於發送方的流量控制。發送方以這兩個窗口中的小值作爲方式上限。


慢啓動算法:指數算法,cwnd默認爲1,當收到一個ack確認時候,cwnd增加爲2,當收到兩個ack確認時候,cwnd增加爲4,接着8,...


擁塞避免算法的目的,是爲了防止中間路由器由於網絡擁塞引起的數據包超時或者丟包。擁塞避免算法需要用到兩個變量,一個是cwnd窗口大小,一個是ssthresh慢啓動閾值,對於一個給定的初始連接,cwnd爲1,ssthresh爲65535。
當擁塞發生(超時或者重複確認),當擁塞發生時候,ssthresh被設置爲cwnd和接收窗口中小值的一半,如果是超時引起的擁塞,則cwnd設置爲1。


擁塞避免算法:如果cwnd大於ssthresh,每收到一個數據報文的確認,cwnd=cwnd+1/cwnd,cwnd窗口大小單位仍然是mss。


擁塞避免算法其實是和慢啓動配合使用的。cwnd和ssthresh都是動態的值,雖然初始值爲1和65535。
當真正擁塞發生的時候,如果是超時或重複ack引起的擁塞,ssthreash會置爲cwnd和接收窗口大小的一半,cwnd會降爲1,然後執行慢啓動算法,直到cwnd大於ssthresh的時候,執行擁塞避免算法;
在慢啓動算法期間和擁塞避免算法期間,TCP的發送速率都是在增長的,只是一個是指數增長方式,一個是線性增長方式。




c . 快速重傳和快速恢復算法
TCP連接中有兩種情況會引起重複的ack,一種是亂序報文,一種是丟包。


快速重傳:當發送方收到三個重複的ack後,不會進入慢啓動狀態,而是立刻重傳丟失的報文。因爲只有接收方收到新的報文段的時候,纔會發送重複的ack,這表明TCP連接上仍然有數據流動,所以應該避免使用慢啓動降速。


快速恢復:
第一步,當收到第三個重複的ack的時候,ssthresh設置爲當前cwnd的一半,重傳丟失的報文。設置cwnd爲ssthresh加上3倍的報文段大小(cwnd=cwnd/2 + 3)。
第二步,每收到一個重複的ack,cwnd增加1併發送一個分組。
第三步,當下一個確認新數據的ack到達的時候,設置cwnd爲上面第一步中ssthresh值,這個ack應該是對重傳報文的確認,同時也是對丟包後面的中間報文的確認。


最後,在收到三個重複ack的情況下,速度減半。


快速重傳算法首次出現在4.3BSD的Tahoe版本,快速恢復首次出現在4.3BSD的Reno版本,也稱之爲Reno版的TCP擁塞控制算法。


可以看出Reno的快速重傳算法是針對一個包的重傳情況的,然而在實際中,一個重傳超時可能導致許多的數據包的重傳,因此當多個數據包從一個數據窗口中丟失時並且觸發快速重傳和快速恢復算法時,問題就產生了。因此NewReno出現了,它在Reno快速恢復的基礎上稍加了修改,可以恢復一個窗口內多個包丟失的情況。具體來講就是:Reno在收到一個新的數據的ACK時就退出了快速恢復狀態了,而NewReno需要收到該窗口內所有數據包的確認後纔會退出快速恢復狀態,從而更一步提高吞吐量。


SACK就是改變TCP的確認機制,最初的TCP只確認當前已連續收到的數據,SACK則把亂序等信息會全部告訴對方,從而減少數據發送方重傳的盲目性。比如說序號1,2,3,5,7的數據收到了,那麼普通的ACK只會確認序列號4,而SACK會把當前的5,7已經收到的信息在SACK選項裏面告知對端,從而提高性能,當使用SACK的時候,NewReno算法可以不使用,因爲SACK本身攜帶的信息就可以使得發送方有足夠的信息來知道需要重傳哪些包,而不需要重傳哪些包。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章