關於 TCP/IP 必須要知道的 10 個問題 (下)

七、DNS
DNS(Domain Name System,網域名稱系統),
網際網路上作為功能變數名稱和IP位址相互映射的一個分散式資料庫,
能夠使使用者更方便的訪問互聯網,而不用去記住能夠被機器直接讀取的IP數串。
通過主機名稱,最終得到該主機名稱對應的IP地址的過程叫做功能變數名稱解析(或主機名稱解析)。
DNS協定運行在UDP協定之上,使用埠號53。
八、TCP連接的建立與終
1、三次握手
 TCP是連線導向的,無論哪一方向另一方發送資料之前,都必須先在雙方之間建立一條連接。
在TCP/IP協定中,TCP協定提供可靠的連接服務,連接是通過三次握手進行初始化的。
三次握手的目的是同步連接雙方的序號和確認號並交換 TCP視窗大小資訊。
第一次握手
建立連接。用戶端發送連接請求報文段,將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的斷開連接,這裡就有了神秘的“四次分手”。
第一次分手
主機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。
從圖中可以看出,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 ,可以使分組注入到網路的速率更加合理。
每經過一個傳輸輪次,擁塞視窗 cwnd 就加倍。
一個傳輸輪次所經歷的時間其實就是往返時間RTT
不過“傳輸輪次”更加強調:把擁塞視窗cwnd所允許發送的報文段都連續發送出去,並收到了對已發送的最後一個位元組的確認。
另,慢開始的“慢”並不是指cwnd的增長速率慢,而是指在TCP開始發送報文段時先設置cwnd=1,
使得發送方在開始時只發送一個報文段(目的是試探一下網路的擁塞情況),
然後再逐漸增大cwnd。為了防止擁塞視窗cwnd增長過大引起網路擁塞,
還需要設置一個慢開始門限ssthresh狀態變數。慢開始門限ssthresh的用法如下:

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

擁塞避免讓擁塞視窗cwnd緩慢地增大,即每經過一個往返時間RTT就把發送方的擁塞視窗cwnd加1,
而不是加倍。這樣擁塞視窗cwnd按線性規律緩慢增長,比慢開始演算法的擁塞視窗增長速率緩慢得多。
無論在慢開始階段還是在擁塞避免階段,只要發送方判斷網路出現擁塞(其根據就是沒有收到確認),
就要把慢開始門限ssthresh設置為出現擁塞時的發送 方視窗值的一半(但不能小於2)。
然後把擁塞視窗cwnd重新設置為1,執行慢開始演算法。
這樣做的目的就是要迅速減少主機發送到網路中的分組數,
使得發生 擁塞的路由器有足夠時間把佇列中積壓的分組處理完畢。
如下圖,用具體數值說明了上述擁塞控制的過程。現在發送視窗的大小和擁塞視窗一樣大。
2、快重傳和快恢復
快重傳
 快重傳演算法首先要求接收方每收到一個失序的報文段後就立即發出重複確認
(為的是使發送方及早知道有報文段沒有到達對方)而不要等到自己發送資料時才進行捎帶確認。
接收方收到了M1和M2後都分別發出了確認。
現在假定接收方沒有收到M3但接著收到了M4。
顯然,接收方不能確認M4,因為M4是收到的失序報文段。
根據 可靠傳輸原理,接收方可以什麼都不做,也可以在適當時機發送一次對M2的確認。
但按照快重傳演算法的規定,接收方應及時發送對M2的重複確認,
這樣做可以讓 發送方及早知道報文段M3沒有到達接收方。
發送方接著發送了M5和M6。接收方收到這兩個報文後,
也還要再次發出對M2的重複確認。
這樣,發送方共收到了 接收方的四個對M2的確認,其中後三個都是重複確認。
快重傳演算法還規定,發送方只要一連收到三個重複確認就應當立即重傳對方尚未收到的報文段M3,
而不必 繼續等待M3設置的重傳計時器到期。
由於發送方儘早重傳未被確認的報文段,因此採用快重傳後可以使整個網路輸送量提高約20%。
快恢復
與快重傳配合使用的還有快恢復演算法,其過程有以下兩個要點:
當發送方連續收到三個重複確認,就執行“乘法減小”演算法,把慢開始門限ssthresh減半。
與慢開始不同之處是現在不執行慢開始演算法(即擁塞視窗cwnd現在不設置為1),
而是把cwnd值設置為 慢開始門限ssthresh減半後的數值,然後開始執行擁塞避免演算法(“加法增大”),使擁塞視窗緩慢地線性增大。