背景描述
通過(guò)上一篇中網(wǎng)絡(luò)模型中的IP層的介紹,我們知道網(wǎng)絡(luò)層,可以實(shí)現(xiàn)兩個(gè)主機(jī)之間的通信。但是這并不具體,因?yàn)?,真正進(jìn)行通信的實(shí)體是在主機(jī)中的進(jìn)程,是一個(gè)主機(jī)中的一個(gè)進(jìn)程與另外一個(gè)主機(jī)中的一個(gè)進(jìn)程在交換數(shù)據(jù)。IP協(xié)議雖然能把數(shù)據(jù)報(bào)文送到目的主機(jī),但是并沒(méi)有交付給主機(jī)的具體應(yīng)用進(jìn)程。而端到端的通信才應(yīng)該是應(yīng)用進(jìn)程之間的通信。UDP,在傳送數(shù)據(jù)前不需要先建立連接,遠(yuǎn)地的主機(jī)在收到UDP報(bào)文后也不需要給出任何確認(rèn)。雖然UDP不提供可靠交付,但是正是因?yàn)檫@樣,省去和很多的開(kāi)銷,使得它的速度比較快,比如一些對(duì)實(shí)時(shí)性要求較高的服務(wù),就常常使用的是UDP。對(duì)應(yīng)的應(yīng)用層的協(xié)議主要有 DNS,TFTP,DHCP,SNMP,NFS 等。TCP,提供面向連接的服務(wù),在傳送數(shù)據(jù)之前必須先建立連接,數(shù)據(jù)傳送完成后要釋放連接。因此TCP是一種可靠的的運(yùn)輸服務(wù),但是正因?yàn)檫@樣,不可避免的增加了許多的開(kāi)銷,比如確認(rèn),流量控制等。對(duì)應(yīng)的應(yīng)用層的協(xié)議主要有 SMTP,TELNET,HTTP,FTP 等。
tcp描述
TCP把連接作為最基本的對(duì)象,每一條TCP連接都有兩個(gè)端點(diǎn),這種斷點(diǎn)我們叫作套接字(socket),它的定義為端口號(hào)拼接到IP地址即構(gòu)成了套接字,例如,若IP地址為192.3.4.16 而端口號(hào)為80,那么得到的套接字為192.3.4.16:80。
tcp報(bào)文首部
- 源端口和目的端口,各占2個(gè)字節(jié),分別寫入源端口和目的端口;
- 序號(hào),占4個(gè)字節(jié),TCP連接中傳送的字節(jié)流中的每個(gè)字節(jié)都按順序編號(hào)。例如,一段報(bào)文的序號(hào)字段值是 301 ,而攜帶的數(shù)據(jù)共有100字段,顯然下一個(gè)報(bào)文段(如果還有的話)的數(shù)據(jù)序號(hào)應(yīng)該從401開(kāi)始;
- 確認(rèn)號(hào),占4個(gè)字節(jié),是期望收到對(duì)方下一個(gè)報(bào)文的第一個(gè)數(shù)據(jù)字節(jié)的序號(hào)。例如,B收到了A發(fā)送過(guò)來(lái)的報(bào)文,其序列號(hào)字段是501,而數(shù)據(jù)長(zhǎng)度是200字節(jié),這表明B正確的收到了A發(fā)送的到序號(hào)700為止的數(shù)據(jù)。因此,B期望收到A的下一個(gè)數(shù)據(jù)序號(hào)是701,于是B在發(fā)送給A的確認(rèn)報(bào)文段中把確認(rèn)號(hào)置為701;
- 數(shù)據(jù)偏移,占4位,它指出TCP報(bào)文的數(shù)據(jù)距離TCP報(bào)文段的起始處有多遠(yuǎn);
- 保留,占6位,保留今后使用,但目前應(yīng)都位0;
- 緊急URG,當(dāng)URG=1,表明緊急指針字段有效。告訴系統(tǒng)此報(bào)文段中有緊急數(shù)據(jù);
- 確認(rèn)ACK,僅當(dāng)ACK=1時(shí),確認(rèn)號(hào)字段才有效。TCP規(guī)定,在連接建立后所有報(bào)文的傳輸都必須把ACK置1;
- 推送PSH,當(dāng)兩個(gè)應(yīng)用進(jìn)程進(jìn)行交互式通信時(shí),有時(shí)在一端的應(yīng)用進(jìn)程希望在鍵入一個(gè)命令后立即就能收到對(duì)方的響應(yīng),這時(shí)候就將PSH=1;
- 復(fù)位RST,當(dāng)RST=1,表明TCP連接中出現(xiàn)嚴(yán)重差錯(cuò),必須釋放連接,然后再重新建立連接;
- 同步SYN,在連接建立時(shí)用來(lái)同步序號(hào)。當(dāng)SYN=1,ACK=0,表明是連接請(qǐng)求報(bào)文,若同意連接,則響應(yīng)報(bào)文中應(yīng)該使SYN=1,ACK=1;
- 終止FIN,用來(lái)釋放連接。當(dāng)FIN=1,表明此報(bào)文的發(fā)送方的數(shù)據(jù)已經(jīng)發(fā)送完畢,并且要求釋放;
- 窗口,占2字節(jié),指的是通知接收方,發(fā)送本報(bào)文你需要有多大的空間來(lái)接受;
- 檢驗(yàn)和,占2字節(jié),校驗(yàn)首部和數(shù)據(jù)這兩部分;
- 緊急指針,占2字節(jié),指出本報(bào)文段中的緊急數(shù)據(jù)的字節(jié)數(shù);
- 選項(xiàng),長(zhǎng)度可變,定義一些其他的可選的參數(shù)。
tcp連接的建立(三次握手)
最開(kāi)始的時(shí)候客戶端和服務(wù)器都是處于CLOSED狀態(tài)。主動(dòng)打開(kāi)連接的為客戶端,被動(dòng)打開(kāi)連接的是服務(wù)器。
- TCP服務(wù)器進(jìn)程先創(chuàng)建傳輸控制塊TCB,時(shí)刻準(zhǔn)備接受客戶進(jìn)程的連接請(qǐng)求,此時(shí)服務(wù)器就進(jìn)入了LISTEN(監(jiān)聽(tīng))狀態(tài);
- TCP客戶進(jìn)程也是先創(chuàng)建傳輸控制塊TCB,然后向服務(wù)器發(fā)出連接請(qǐng)求報(bào)文,這是報(bào)文首部中的同部位SYN=1,同時(shí)選擇一個(gè)初始序列號(hào) seq=x ,此時(shí),TCP客戶端進(jìn)程進(jìn)入了 SYN-SENT(同步已發(fā)送狀態(tài))狀態(tài)。TCP規(guī)定,SYN報(bào)文段(SYN=1的報(bào)文段)不能攜帶數(shù)據(jù),但需要消耗掉一個(gè)序號(hào)。
- TCP服務(wù)器收到請(qǐng)求報(bào)文后,如果同意連接,則發(fā)出確認(rèn)報(bào)文。確認(rèn)報(bào)文中應(yīng)該 ACK=1,SYN=1,確認(rèn)號(hào)是ack=x+1,同時(shí)也要為自己初始化一個(gè)序列號(hào) seq=y,此時(shí),TCP服務(wù)器進(jìn)程進(jìn)入了SYN-RCVD(同步收到)狀態(tài)。這個(gè)報(bào)文也不能攜帶數(shù)據(jù),但是同樣要消耗一個(gè)序號(hào)。
- TCP客戶進(jìn)程收到確認(rèn)后,還要向服務(wù)器給出確認(rèn)。確認(rèn)報(bào)文的ACK=1,ack=y+1,自己的序列號(hào)seq=x+1,此時(shí),TCP連接建立,客戶端進(jìn)入ESTABLISHED(已建立連接)狀態(tài)。TCP規(guī)定,ACK報(bào)文段可以攜帶數(shù)據(jù),但是如果不攜帶數(shù)據(jù)則不消耗序號(hào)。
- 當(dāng)服務(wù)器收到客戶端的確認(rèn)后也進(jìn)入ESTABLISHED狀態(tài),此后雙方就可以開(kāi)始通信了。
為什么TCP客戶端最后還要發(fā)送一次確認(rèn)呢?
一句話,主要防止已經(jīng)失效的連接請(qǐng)求報(bào)文突然又傳送到了服務(wù)器,從而產(chǎn)生錯(cuò)誤。如果使用的是兩次握手建立連接,假設(shè)有這樣一種場(chǎng)景,客戶端發(fā)送了第一個(gè)請(qǐng)求連接并且沒(méi)有丟失,只是因?yàn)樵诰W(wǎng)絡(luò)結(jié)點(diǎn)中滯留的時(shí)間太長(zhǎng)了,由于TCP的客戶端遲遲沒(méi)有收到確認(rèn)報(bào)文,以為服務(wù)器沒(méi)有收到,此時(shí)重新向服務(wù)器發(fā)送這條報(bào)文,此后客戶端和服務(wù)器經(jīng)過(guò)兩次握手完成連接,傳輸數(shù)據(jù),然后關(guān)閉連接。此時(shí)此前滯留的那一次請(qǐng)求連接,網(wǎng)絡(luò)通暢了到達(dá)了服務(wù)器,這個(gè)報(bào)文本該是失效的,但是,兩次握手的機(jī)制將會(huì)讓客戶端和服務(wù)器再次建立連接,這將導(dǎo)致不必要的錯(cuò)誤和資源的浪費(fèi)。如果采用的是三次握手,就算是那一次失效的報(bào)文傳送過(guò)來(lái)了,服務(wù)端接受到了那條失效報(bào)文并且回復(fù)了確認(rèn)報(bào)文,但是客戶端不會(huì)再次發(fā)出確認(rèn)。由于服務(wù)器收不到確認(rèn),就知道客戶端并沒(méi)有請(qǐng)求連接
tcp連接的釋放(四次揮手)
數(shù)據(jù)傳輸完畢后,雙方都可釋放連接。最開(kāi)始的時(shí)候,客戶端和服務(wù)器都是處于ESTABLISHED狀態(tài),然后客戶端主動(dòng)關(guān)閉,服務(wù)器被動(dòng)關(guān)閉。
- 客戶端進(jìn)程發(fā)出連接釋放報(bào)文,并且停止發(fā)送數(shù)據(jù)。釋放數(shù)據(jù)報(bào)文首部,F(xiàn)IN=1,其序列號(hào)為seq=u(等于前面已經(jīng)傳送過(guò)來(lái)的數(shù)據(jù)的最后一個(gè)字節(jié)的序號(hào)加1),此時(shí),客戶端進(jìn)入FIN-WAIT-1(終止等待1)狀態(tài)。 TCP規(guī)定,F(xiàn)IN報(bào)文段即使不攜帶數(shù)據(jù),也要消耗一個(gè)序號(hào)。
- 服務(wù)器收到連接釋放報(bào)文,發(fā)出確認(rèn)報(bào)文,ACK=1,ack=u+1,并且?guī)献约旱男蛄刑?hào)seq=v,此時(shí),服務(wù)端就進(jìn)入了CLOSE-WAIT(關(guān)閉等待)狀態(tài)。TCP服務(wù)器通知高層的應(yīng)用進(jìn)程,客戶端向服務(wù)器的方向就釋放了,這時(shí)候處于半關(guān)閉狀態(tài),即客戶端已經(jīng)沒(méi)有數(shù)據(jù)要發(fā)送了,但是服務(wù)器若發(fā)送數(shù)據(jù),客戶端依然要接受。這個(gè)狀態(tài)還要持續(xù)一段時(shí)間,也就是整個(gè)CLOSE-WAIT狀態(tài)持續(xù)的時(shí)間。
- 客戶端收到服務(wù)器的確認(rèn)請(qǐng)求后,此時(shí),客戶端就進(jìn)入FIN-WAIT-2(終止等待2)狀態(tài),等待服務(wù)器發(fā)送連接釋放報(bào)文(在這之前還需要接受服務(wù)器發(fā)送的最后的數(shù)據(jù))。
- 服務(wù)器將最后的數(shù)據(jù)發(fā)送完畢后,就向客戶端發(fā)送連接釋放報(bào)文,F(xiàn)IN=1,ack=u+1,由于在半關(guān)閉狀態(tài),服務(wù)器很可能又發(fā)送了一些數(shù)據(jù),假定此時(shí)的序列號(hào)為seq=w,此時(shí),服務(wù)器就進(jìn)入了LAST-ACK(最后確認(rèn))狀態(tài),等待客戶端的確認(rèn)。
- 客戶端收到服務(wù)器的連接釋放報(bào)文后,必須發(fā)出確認(rèn),ACK=1,ack=w+1,而自己的序列號(hào)是seq=u+1,此時(shí),客戶端就進(jìn)入了TIME-WAIT(時(shí)間等待)狀態(tài)。注意此時(shí)TCP連接還沒(méi)有釋放,必須經(jīng)過(guò)2??*MSL(最長(zhǎng)報(bào)文段壽命)的時(shí)間后,當(dāng)客戶端撤銷相應(yīng)的TCB后,才進(jìn)入CLOSED狀態(tài)。
- 服務(wù)器只要收到了客戶端發(fā)出的確認(rèn),立即進(jìn)入CLOSED狀態(tài)。同樣,撤銷TCB后,就結(jié)束了這次的TCP連接??梢钥吹?,服務(wù)器結(jié)束TCP連接的時(shí)間要比客戶端早一些。
為什么客戶端最后還要等待2MSL?
MSL(Maximum Segment Lifetime),TCP允許不同的實(shí)現(xiàn)可以設(shè)置不同的MSL值。第一,保證客戶端發(fā)送的最后一個(gè)ACK報(bào)文能夠到達(dá)服務(wù)器,因?yàn)檫@個(gè)ACK報(bào)文可能丟失,站在服務(wù)器的角度看來(lái),我已經(jīng)發(fā)送了FIN+ACK報(bào)文請(qǐng)求斷開(kāi)了,客戶端還沒(méi)有給我回應(yīng),應(yīng)該是我發(fā)送的請(qǐng)求斷開(kāi)報(bào)文它沒(méi)有收到,于是服務(wù)器又會(huì)重新發(fā)送一次,而客戶端就能在這個(gè)2MSL時(shí)間段內(nèi)收到這個(gè)重傳的報(bào)文,接著給出回應(yīng)報(bào)文,并且會(huì)重啟2MSL計(jì)時(shí)器。第二,防止類似與“三次握手”中提到了的“已經(jīng)失效的連接請(qǐng)求報(bào)文段”出現(xiàn)在本連接中??蛻舳税l(fā)送完最后一個(gè)確認(rèn)報(bào)文后,在這個(gè)2MSL時(shí)間中,就可以使本連接持續(xù)的時(shí)間內(nèi)所產(chǎn)生的所有報(bào)文段都從網(wǎng)絡(luò)中消失。這樣新的連接中不會(huì)出現(xiàn)舊連接的請(qǐng)求報(bào)文。為什么建立連接是三次握手,關(guān)閉連接確是四次揮手呢?建立連接的時(shí)候, 服務(wù)器在LISTEN狀態(tài)下,收到建立連接請(qǐng)求的SYN報(bào)文后,把ACK和SYN放在一個(gè)報(bào)文里發(fā)送給客戶端。 而關(guān)閉連接時(shí),服務(wù)器收到對(duì)方的FIN報(bào)文時(shí),僅僅表示對(duì)方不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù),而自己也未必全部數(shù)據(jù)都發(fā)送給對(duì)方了,所以己方可以立即關(guān)閉,也可以發(fā)送一些數(shù)據(jù)給對(duì)方后,再發(fā)送FIN報(bào)文給對(duì)方來(lái)表示同意現(xiàn)在關(guān)閉連接,因此,己方ACK和FIN一般都會(huì)分開(kāi)發(fā)送,從而導(dǎo)致多了一次。
如果已經(jīng)建立了連接,但是客戶端突然出現(xiàn)故障了怎么辦?
TCP還設(shè)有一個(gè)?;钣?jì)時(shí)器,顯然,客戶端如果出現(xiàn)故障,服務(wù)器不能一直等下去,白白浪費(fèi)資源。服務(wù)器每收到一次客戶端的請(qǐng)求后都會(huì)重新復(fù)位這個(gè)計(jì)時(shí)器,時(shí)間通常是設(shè)置為2小時(shí),若兩小時(shí)還沒(méi)有收到客戶端的任何數(shù)據(jù),服務(wù)器就會(huì)發(fā)送一個(gè)探測(cè)報(bào)文段,以后每隔75分鐘發(fā)送一次。若一連發(fā)送10個(gè)探測(cè)報(bào)文仍然沒(méi)反應(yīng),服務(wù)器就認(rèn)為客戶端出了故障,接著就關(guān)閉連接。