天天看點

TCP與傳輸層UDPTCP

傳輸層協定主要有兩個,TCP和UDP。

 TCP即傳輸控制協定,是一個可靠的、面向連接配接的協定。它允許網絡間兩台主機之間無差錯的資訊傳輸。TCP協定還進行流量控制,以避免發送過快而發生擁塞。不過這一切對使用者都是透明的。

UDP即使用者資料報協定,它采用無連接配接的方式傳送資料,也就是說發送端不關心發送的資料是否到達目标主機,資料是否出錯等。收到資料的主機也不會告訴發送方是否收到了資料,它的可靠性由上層協定來保障。

這兩個協定針對不同網絡環境實作資料傳輸,各有優缺點。面向連接配接的TCP協定效率較低,但可靠性高,适合于網絡鍊路不好或可靠性要求高的環境;UDP面向非連接配接,不可靠,但因為不用傳送許多與資料本身無關的資訊,是以效率較高,常用于一些實時業務,也用于一些對差錯不敏感的應用。這樣就可以在不同的場合和要求下選用不同的協定,達到預期通信目标。下面分别對TCP和UDP進行詳細介紹。

UDP

TCP協定的資料傳輸比較可靠,但是以而付出的代價在某些時候顯得不太合适。比如主機A想要給主機B發送一句話“你好”,這個資料傳輸僅僅為4個位元組而已。但用TCP協定來封裝、傳輸它,至少要加上一個20個位元組的首部,還要為此建立一個3次握手的連接配接。并且在資料傳輸完畢後還要進行4次斷開。這樣看來似乎對于一些簡短的資料傳輸可以設計一種簡單的傳輸協定。忽略一些可靠性考慮,提高資料傳輸率。在這種背景下,傳輸層的另一個協定UDP産生了。

UDP是一個簡單的面向資料包的傳輸層協定,程序的每個輸出操作都恰好産生一個UDP資料段,并組裝成一份待發送的IP資料包。

UDP不提供可靠性:它把應用程式傳給IP層的資料發送出去,但是并不保證它們能到達目的地。由于缺乏可靠性,似乎應該避免使用UDP而使用一種可靠的協定如TCP。可是在小資料檔案的傳輸中,UDP發揮了重要的作用。例如,當發送一個10位元組的短消息時;使用TCP和UDP都可以。可是TCP會加上至少20位元組的頭部資料,而且還要使用确認,這樣資料的傳輸效率就較低。如果使用UDP則要加上的首部就縮減到8位元組,并且沒有确認。這就使得資料傳輸率大幅度提高。當然,這樣提高效率付出的代價是資料傳輸的可靠性得不到保障。

TCP

TCP是面向連接配接的可靠協定,TCP協定為實作可靠的資料傳輸而提供了一系列的方法和手段。

首先,TCP的資料段采取編号的方式保證資料的正确順序。TCP資料段被封裝在IP資料包中來完成傳輸,而IP資料包經過的路徑有可能不同,那麼IP資料包到達時可能會失去原有順序,因而到達的TCP資料段也可能會失序。為了解決這個問題,TCP對資料段進行編号。對接收到的資料進行重新排序,然後以正确的順序交給應用層。

其次,由于到達的IP資料包有可能會發生重複,是以TCP的接收端必須有丢棄重複資料的功能。

再次,TCP提供流量控制。

TCP連接配接的每一方都有固定大小的緩沖空間,這就要求TCP的發送端隻發送接收端緩沖區能接收下的資料。這将防止較快的主機導緻較慢的主機緩沖區溢出。

上面的每一個功能都是針對每一個問題而設計的,TCP協定的各種功能的實作依賴于它的首部資料結構。在TCP的首部中包含了許多TCP資料段的重要資訊。

一、TCP連接配接的建立與終止

1、三次握手

TCP是面向連接配接的,無論哪一方向另一方發送資料之前,都必須先在雙方之間建立一條連接配接。在TCP/IP協定中,TCP協定提供可靠的連接配接服務,連接配接是通過三次握手進行初始化的。三次握手的目的是同步連接配接雙方的序列号和确認号并交換 TCP視窗大小資訊。

TCP與傳輸層UDPTCP

「第一次握手」:建立連接配接。用戶端發送連接配接請求封包段,将SYN位置為1,Sequence Number為x;然後,用戶端進入SYN_SEND狀态,等待伺服器的确認;

「第二次握手」:伺服器收到SYN封包段。伺服器收到用戶端的SYN封包段,需要對這個SYN封包段進行确認,設定Acknowledgment Number為x+1(Sequence Number+1);同時,自己自己還要發送SYN請求資訊,将SYN位置為1,Sequence Number為y;伺服器端将上述所有資訊放到一個封包段(即SYN+ACK封包段)中,一并發送給用戶端,此時伺服器進入SYN_RECV狀态;

「第三次握手」:用戶端收到伺服器的SYN+ACK封包段。然後将Acknowledgment Number設定為y+1,向伺服器發送ACK封包段,這個封包段發送完畢以後,用戶端和伺服器端都進入ESTABLISHED狀态,完成TCP三向交握。

 為什麼要三次握手而不是二次或四次?

 為了防止已失效的連接配接請求封包段突然又傳送到了服務端,因而産生錯誤。

具體例子:“已失效的連接配接請求封包段”的産生在這樣一種情況下:client發出的第一個連接配接請求封包段并沒有丢失,而是在某個網絡結點長時間的滞留了,以緻延誤到連接配接釋放以後的某個時間才到達server。本來這是一個早已失效的封包段。但server收到此失效的連接配接請求封包段後,就誤認為是client再次發出的一個新的連接配接請求。

于是就向client發出确認封包段,同意建立連接配接。假設不采用“三次握手”,那麼隻要server發出确認,新的連接配接就建立了。由于現在client并沒有發出建立連接配接的請求,是以不會理睬server的确認,也不會向server發送資料。但server卻以為新的運輸連接配接已經建立,并一直等待client發來資料。這樣,server的很多資源就白白浪費掉了。采用“三次握手”的辦法可以防止上述現象發生。例如剛才那種情況,client不會向server的确認發出确認。server由于收不到确認,就知道client并沒有要求建立連接配接。”

2、四次揮手

當用戶端和伺服器通過三次握手建立了TCP連接配接以後,當資料傳送完畢,就要斷開TCP連接配接。對于TCP的斷開連接配接,就有了“四次揮手”。

TCP與傳輸層UDPTCP

「第一次揮手」:主機1(可以使用戶端,也可以是伺服器端),設定Sequence Number,向主機2發送一個FIN封包段;此時,主機1進入FIN_WAIT_1狀态;這表示主機1沒有資料要發送給主機2了;

「第二次分手」:主機2收到了主機1發送的FIN封包段,向主機1回一個ACK封包段,Acknowledgment Number為Sequence Number加1;主機1進入FIN_WAIT_2狀态;主機2告訴主機1,我“同意”你的關閉請求;

「第三次分手」:主機2向主機1發送FIN封包段,請求關閉連接配接,同時主機2進入LAST_ACK狀态;

「第四次分手」:主機1收到主機2發送的FIN封包段,向主機2發送ACK封包段,然後主機1進入TIME_WAIT狀态;主機2收到主機1的ACK封包段以後,就關閉連接配接;此時,主機1等待2MSL後依然沒有收到回複,則證明Server端已正常關閉,那麼主機1也可以關閉連接配接了。

 為什麼要四次揮手而不是其他次?

TCP協定是一種面向連接配接的、可靠的、基于位元組流的運輸層通信協定。TCP是全雙工模式,這就意味着,當主機1發出FIN封包段時,隻是表示主機1已經沒有資料要發送了,主機1告訴主機2,它的資料已經全部發送完畢了;但是,這個時候主機1還是可以接受來自主機2的資料;當主機2傳回ACK封包段時,表示它已經知道主機1沒有資料發送了,但是主機2還是可以發送資料到主機1的;當主機2也發送了FIN封包段時,這個時候就表示主機2也沒有資料要發送了,就會告訴主機1,我也沒有資料要發送了,之後彼此就會愉快的中斷這次TCP連接配接。

為什麼要等待2MSL?

MSL:封包段最大生存時間,它是任何封包段被丢棄前在網絡内的最長時間。原因有二:

  • 保證TCP協定的全雙工連接配接能夠可靠關閉
  • 保證這次連接配接的重複資料段從網絡中消失

第一點:如果主機1直接CLOSED了,那麼由于IP協定的不可靠性或者是其它網絡原因,導緻主機2沒有收到主機1最後回複的ACK。那麼主機2就會在逾時之後繼續發送FIN,此時由于主機1已經CLOSED了,就找不到與重發的FIN對應的連接配接。是以,主機1不是直接進入CLOSED,而是要保持TIME_WAIT,當再次收到FIN的時候,能夠保證對方收到ACK,最後正确的關閉連接配接。

第二點:如果主機1直接CLOSED,然後又再向主機2發起一個新連接配接,我們不能保證這個新連接配接與剛關閉的連接配接的端口号是不同的。也就是說有可能新連接配接和老連接配接的端口号是相同的。這樣可能會出現問題:假設新連接配接和已經關閉的老連接配接端口号是一樣的,如果前一次連接配接的某些資料仍然滞留在網絡中,這些延遲資料在建立新連接配接之後才到達主機2,由于新連接配接和老連接配接的端口号是一樣的,TCP協定就認為那個延遲的資料是屬于新連接配接的,這樣就和真正的新連接配接的資料包發生混淆了。是以TCP連接配接還要在TIME_WAIT狀态等待2倍MSL,這樣可以保證本次連接配接的所有資料都從網絡中消失。

二、TCP流量控制

如果發送方把資料發送得過快,接收方可能會來不及接收,這就會造成資料的丢失。所謂流量控制就是讓發送方的發送速率不要太快,要讓接收方來得及接收。

利用「滑動視窗機制」可以很友善地在TCP連接配接上實作對發送方的流量控制。

設A向B發送資料。在連接配接建立時,B告訴了A:“我的接收視窗是 rwnd = 400 ”(這裡的 rwnd 表示 receiver window) 。是以,發送方的發送視窗不能超過接收方給出的接收視窗的數值。TCP的視窗機關是位元組,不是封包段。假設每一個封包段為100位元組長,而資料封包段序号的初始值設為1。大寫ACK表示首部中的确認位ACK,小寫ack表示确認字段的值ack。

TCP與傳輸層UDPTCP

從圖中可以看出,B進行了三次流量控制。第一次把視窗減少到 rwnd = 300 ,第二次又減到了 rwnd = 100 ,最後減到 rwnd = 0 ,即不允許發送方再發送資料了。這種使發送方暫停發送的狀态将持續到主機B重新發出一個新的視窗值為止。B向A發送的三個封包段都設定了 ACK = 1 ,隻有在ACK=1時确認号字段才有意義。

TCP為每一個連接配接設有一個持續計時器(persistence timer)。隻要TCP連接配接的一方收到對方的零視窗通知,就啟動持續計時器。若持續計時器設定的時間到期,就發送一個零視窗控測封包段(攜1位元組的資料),那麼收到這個封包段的一方就重新設定持續計時器。

 三、TCP擁塞控制

發送方維持一個擁塞視窗 cwnd ( congestion window )的狀态變量。擁塞視窗的大小取決于網絡的擁塞程度,并且動态地在變化。發送方讓自己的發送視窗等于擁塞視窗。

發送方控制擁塞視窗的原則是:隻要網絡沒有出現擁塞,擁塞視窗就再增大一些,以便把更多的分組發送出去。但隻要網絡出現擁塞,擁塞視窗就減小一些,以減少注入到網絡中的分組數。

慢開始算法:

當主機開始發送資料時,如果立即所大量資料位元組注入到網絡,那麼就有可能引起網絡擁塞,因為現在并不清楚網絡的負荷情況。是以,較好的方法是先探測一下,即由小到大逐漸增大發送視窗,也就是說,由小到大逐漸增大擁塞視窗數值。

通常在剛剛開始發送封包段時,先把擁塞視窗 cwnd 設定為一個最大封包段MSS的數值。而在每收到一個對新的封包段的确認後,把擁塞視窗增加至多一個MSS的數值。用這樣的方法逐漸增大發送方的擁塞視窗 cwnd ,可以使分組注入到網絡的速率更加合理。

TCP與傳輸層UDPTCP

每經過一個傳輸輪次,擁塞視窗 cwnd 就加倍。一個傳輸輪次所經曆的時間其實就是往返時間「RTT」。不過“傳輸輪次”更加強調:把擁塞視窗cwnd所允許發送的封包段都連續發送出去,并收到了對已發送的最後一個位元組的确認。

另,慢開始的“慢”并不是指cwnd的增長速率慢,而是指在TCP開始發送封包段時先設定cwnd=1,使得發送方在開始時隻發送一個封包段(目的是試探一下網絡的擁塞情況),然後再逐漸增大cwnd。

為了防止擁塞視窗cwnd增長過大引起網絡擁塞,還需要設定一個慢開始門限ssthresh狀态變量。慢開始門限ssthresh的用法如下:

  • 當 cwnd < ssthresh 時,使用上述的慢開始算法。
  • 當 cwnd > ssthresh 時,停止使用慢開始算法而改用擁塞避免算法。
  • 當 cwnd = ssthresh 時,既可使用慢開始算法,也可使用擁塞控制避免算法。擁塞避免

 擁塞避免

讓擁塞視窗cwnd緩慢地增大,即每經過一個往返時間RTT就把發送方的擁塞視窗cwnd加1,而不是加倍。這樣擁塞視窗cwnd按線性規律緩慢增長,比慢開始算法的擁塞視窗增長速率緩慢得多。

TCP與傳輸層UDPTCP

 無論在慢開始階段還是在擁塞避免階段,隻要發送方判斷網絡出現擁塞(其根據就是沒有收到确認),就要把慢開始門限ssthresh設定為出現擁塞時的發送方視窗值的一半(但不能小于2)。然後把擁塞視窗cwnd重新設定為1,執行慢開始算法。

這樣做的目的就是要迅速減少主機發送到網絡中的分組數,使得發生擁塞的路由器有足夠時間把隊列中積壓的分組處理完畢。如下圖,用具體數值說明了上述擁塞控制的過程。現在發送視窗的大小和擁塞視窗一樣大。

TCP與傳輸層UDPTCP

快重傳

快重傳算法首先要求接收方每收到一個失序的封包段後就立即發出重複确認(為的是使發送方及早知道有封包段沒有到達對方)而不要等到自己發送資料時才進行捎帶确認。

TCP與傳輸層UDPTCP

接收方收到了M1和M2後都分别發出了确認。現在假定接收方沒有收到M3但接着收到了M4。顯然,接收方不能确認M4,因為M4是收到的失序封包段。根據 可靠傳輸原理,接收方可以什麼都不做,也可以在适當時機發送一次對M2的确認。

但按照快重傳算法的規定,接收方應及時發送對M2的重複确認,這樣做可以讓發送方及早知道封包段M3沒有到達接收方。發送方接着發送了M5和M6。接收方收到這兩個封包後,也還要再次發出對M2的重複确認。這樣,發送方共收到了接收方的四個對M2的确認,其中後三個都是重複确認。

快重傳算法還規定,發送方隻要一連收到三個重複确認就應當立即重傳對方尚未收到的封包段M3,而不必繼續等待M3設定的重傳計時器到期。由于發送方盡早重傳未被确認的封包段,是以采用快重傳後可以使整個網絡吞吐量提高約20%。

 快恢複

與快重傳配合使用的還有快恢複算法,其過程如下:

  • 當發送方連續收到三個重複确認,就執行“乘法減小”算法,把慢開始門限ssthresh減半。
  • 與慢開始不同之處是現在不執行慢開始算法(即擁塞視窗cwnd現在不設定為1),而是把cwnd值設定為慢開始門限ssthresh減半後的數值,然後開始執行擁塞避免算法(“加法增大”),使擁塞視窗緩慢地線性增大。

繼續閱讀