TCP協(xié)議總結(jié)

Transmission Control Protocol,傳輸控制協(xié)議,是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議

簡(jiǎn)述

  • 通過(guò)三次握手,四次揮手達(dá)到面向連接
  • 通過(guò)校驗(yàn)和確保數(shù)據(jù)在傳輸過(guò)程中沒(méi)有被修改
  • 通過(guò)序號(hào)確保順序
  • 通過(guò)確認(rèn)機(jī)制+重傳機(jī)制確保數(shù)據(jù)到達(dá)
  • 通過(guò)流量窗口+擁塞控制機(jī)制確保通信質(zhì)量

原理與應(yīng)用

TCP協(xié)議的目的是:在不可靠傳輸?shù)腎P層之上建立一套可靠傳輸?shù)臋C(jī)制。 TCP的可靠只是對(duì)于它自身來(lái)說(shuō)的, 甚至是對(duì)于socket接口層, 兩個(gè)系統(tǒng)就不是可靠的了, 因?yàn)榘l(fā)送出去的數(shù)據(jù), 沒(méi)有確保對(duì)方真正的讀到(所以要在業(yè)務(wù)層做重傳和確認(rèn)機(jī)制)。

可靠傳輸?shù)牡谝灰厥?strong>確認(rèn), 第二要素是重傳, 第三要素是順序。 任何一個(gè)可靠傳輸?shù)南到y(tǒng), 都必須包含這三個(gè)要素。數(shù)據(jù)校驗(yàn)也是必要的。

傳輸是一個(gè)廣義的概念, 不局限于狹義的網(wǎng)絡(luò)傳輸, 應(yīng)該理解為通信和交互. 任何涉及到通信和交互的東西, 都可以借鑒TCP的思想。無(wú)論是在UDP上實(shí)現(xiàn)可靠傳輸或者創(chuàng)建自己的通信系統(tǒng),無(wú)論這個(gè)系統(tǒng)是以API方式還是服務(wù)方式,只要是一個(gè)通信系統(tǒng),就要考慮這三個(gè)要素。

TCP頭結(jié)構(gòu)

  • 需要四個(gè)元組(src_ip, src_port, dest_ip, dest_port)來(lái)表示同一個(gè)連接。準(zhǔn)確的說(shuō)是五個(gè)元組,還有一個(gè)是協(xié)議,可以同一個(gè)IP,同一臺(tái)機(jī)器,同時(shí)使用TCP和UDP監(jiān)聽(tīng)同一個(gè)端口,因?yàn)镮P header中有個(gè)字段是指定使用哪個(gè)協(xié)議的
  • Sequence Number:數(shù)據(jù)包的序號(hào),用來(lái)解決網(wǎng)絡(luò)亂序問(wèn)題
  • Acknowledgement Number:簡(jiǎn)稱ACK,用來(lái)確認(rèn)數(shù)據(jù)包是否已收到,解決數(shù)據(jù)包丟失問(wèn)題
  • Window:又叫Advertised-Window,也就是著名的滑動(dòng)窗口(Sliding Window),用于解決流控
  • Checksum:校驗(yàn)和,用于檢查數(shù)據(jù)包在傳輸過(guò)程中是否被修改過(guò)
  • TCP Flag:數(shù)據(jù)包的類型,主要是用于操控TCP的狀態(tài)機(jī)

數(shù)據(jù)傳輸中的序號(hào)

SeqNum的增加是和傳輸?shù)淖止?jié)數(shù)相關(guān)的。上圖中,三次握手后,來(lái)了兩個(gè)Len:1440的包,而第二個(gè)包的SeqNum就成了1441。然后第一個(gè)ACK回的是1441(下一個(gè)待接收的字節(jié)號(hào)),表示第一個(gè)1440收到了。

TCP狀態(tài)機(jī)

網(wǎng)絡(luò)上的傳輸是沒(méi)有連接的,包括TCP也是一樣的。而TCP所謂的“連接”,其實(shí)只不過(guò)是在通訊的雙方維護(hù)一個(gè)“連接狀態(tài)”,讓它看上去好像有連接一樣。所以,TCP的狀態(tài)變換是非常重要的。

狀態(tài)表

狀態(tài) 說(shuō)明
CLOSED 關(guān)閉狀態(tài),沒(méi)有連接活動(dòng)
LISTEN 監(jiān)聽(tīng)狀態(tài),服務(wù)器正在等待連接進(jìn)入
SYN_SENT 已經(jīng)發(fā)出連接請(qǐng)求,等待確認(rèn)
SYN_RCVD 收到一個(gè)連接請(qǐng)求,尚未確認(rèn)
ESTABLISHED 連接建立,正常數(shù)據(jù)傳輸狀態(tài)
FIN_WAIT_1 (主動(dòng)關(guān)閉)已經(jīng)發(fā)送關(guān)閉請(qǐng)求,等待確認(rèn)
FIN_WAIT_2 (主動(dòng)關(guān)閉)收到對(duì)方關(guān)閉確認(rèn),等待對(duì)方關(guān)閉請(qǐng)求
CLOSE_WAIT (被動(dòng)關(guān)閉)收到對(duì)方關(guān)閉請(qǐng)求,已經(jīng)確認(rèn)
LAST_ACK (被動(dòng)關(guān)閉)等待最后一個(gè)關(guān)閉確認(rèn),并等待所有分組死掉
TIMED_WAIT 完成雙向關(guān)閉,等待所有分組死掉
CLOSING 雙方同時(shí)嘗試關(guān)閉,等待對(duì)方確認(rèn)

查看各種狀態(tài)的數(shù)量
ss -ant | awk '{++s[$1]} END {for(k in s) print k,s[k]}'

建立連接

三次握手

通過(guò)三次握手完成連接的建立

  1. 客戶端發(fā)送SYN(x)
  2. 服務(wù)端返回ACK(x+1), SYN(y)
  3. 客戶端返回ACK(y+1)

三次握手的目的是交換通信雙方的初始化序號(hào),以保證應(yīng)用層接收到的數(shù)據(jù)不會(huì)亂序,所以叫SYN(Synchronize Sequence Numbers)。

為什么要三次握手

  1. 問(wèn)題的本質(zhì)是:信道不可靠,但通信雙方需要就某個(gè)問(wèn)題達(dá)成一致。而要解決這個(gè)問(wèn)題,三次通信是理論的最小值。原則上任何數(shù)據(jù)傳輸都無(wú)法確保絕對(duì)可靠,三次握手只是確??煽康幕拘枰ㄈ我呀?jīng)能夠保證足夠高的可靠性概率,四次就有點(diǎn)多余)
  2. 通信雙方都需要確認(rèn)自己的發(fā)信和收信功能正常,其中發(fā)信功能需要發(fā)出信息并得到對(duì)方確認(rèn),收信功能通過(guò)接收對(duì)方信息得到確認(rèn)。最簡(jiǎn)單的例子就是雙方打電話,一方先問(wèn):“喂,聽(tīng)到嗎?”,另外一方收到后(此時(shí)確定自己接聽(tīng)沒(méi)有問(wèn)題,但不知說(shuō)話是否有問(wèn)題),回復(fù):“聽(tīng)得到,你呢,能聽(tīng)到我說(shuō)話嗎?“,第一個(gè)人聽(tīng)到后(確定自己的發(fā)送和接收都沒(méi)有問(wèn)題)回復(fù):”聽(tīng)得到“,另外一方收到后確定自己的發(fā)送也是沒(méi)有問(wèn)題的,之后雙方就可以愉快地談話了。


  3. 只通信兩次不行,因?yàn)橹煌ㄐ艃纱危锌赡茉斐梢咽У倪B接請(qǐng)求報(bào)文段突然又傳送到了服務(wù)端,而產(chǎn)生錯(cuò)誤的問(wèn)題:client發(fā)出的第一個(gè)連接請(qǐng)求報(bào)文段并沒(méi)有丟失,而是在某個(gè)網(wǎng)絡(luò)結(jié)點(diǎn)長(zhǎng)時(shí)間的滯留了,以致延誤到連接釋放以后的某個(gè)時(shí)間才到達(dá)server。本來(lái)這是一個(gè)早已失效的報(bào)文段。但server收到此失效的連接請(qǐng)求報(bào)文段后,就誤認(rèn)為是client再次發(fā)出的一個(gè)新的連接請(qǐng)求。于是就向client發(fā)出確認(rèn)報(bào)文段,同意建立連接。假設(shè)不采用“三次握手”,那么只要server發(fā)出確認(rèn),新的連接就建立了。由于現(xiàn)在client并沒(méi)有發(fā)出建立連接的請(qǐng)求,因此不會(huì)理睬server的確認(rèn),也不會(huì)向server發(fā)送數(shù)據(jù)。但server卻以為新的運(yùn)輸連接已經(jīng)建立,并一直等待client發(fā)來(lái)數(shù)據(jù)。這樣,server的很多資源就白白浪費(fèi)掉了。而采用三次握手可以避免此問(wèn)題,client不會(huì)向server的確認(rèn)發(fā)出確認(rèn)。server由于收不到確認(rèn),就知道client并沒(méi)有要求建立連接。

ISN初始化

ISN是不能hard code的,不然會(huì)出問(wèn)題的。比如:如果連接建好后始終用1來(lái)做ISN,如果client發(fā)了30個(gè)segment過(guò)去,但是網(wǎng)絡(luò)斷了,于是client重連,又用了1做ISN,但是之前連接的那些包到了,于是就被當(dāng)成了新連接的包,此時(shí),client的Sequence Number可能是3,而Server端認(rèn)為client端的這個(gè)號(hào)是30了。全亂了。RFC793中說(shuō),ISN會(huì)和一個(gè)假的時(shí)鐘綁在一起,這個(gè)時(shí)鐘會(huì)在每4微秒對(duì)ISN做加一操作,直到超過(guò)232,又從0開(kāi)始。這樣,一個(gè)ISN的周期大約是4.55個(gè)小時(shí)。因?yàn)?,我們假設(shè)我們的TCP Segment在網(wǎng)絡(luò)上的存活時(shí)間不會(huì)超過(guò)Maximum Segment Lifetime(MSL),所以,只要MSL的值小于4.55小時(shí),那么,我們就不會(huì)重用到ISN。

SYN超時(shí)

如果Server端接到了Clien發(fā)的SYN后回了SYN-ACK,之后Client掉線了,Server端沒(méi)有收到Client返回的ACK,那么,這個(gè)連接就處于一個(gè)中間狀態(tài),即沒(méi)成功,也沒(méi)失敗。于是,Server端如果在一定時(shí)間內(nèi)沒(méi)有收到的ACK會(huì)重發(fā)SYN-ACK。在Linux下,默認(rèn)重試次數(shù)為5次,重試的間隔時(shí)間從1s開(kāi)始每次都翻番,5次的重試時(shí)間間隔為1s, 2s, 4s, 8s, 16s,總共31s,第5次發(fā)出后還要等32s都知道第5次也超時(shí)了,所以,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 26 -1 = 63s,TCP才會(huì)斷開(kāi)這個(gè)連接。

SYN Flood攻擊

客戶端給服務(wù)器發(fā)了一個(gè)SYN后,就下線了,于是服務(wù)器需要默認(rèn)等63s才會(huì)斷開(kāi)連接,這樣,攻擊者就可以把服務(wù)器的SYN連接的隊(duì)列耗盡,讓正常的連接請(qǐng)求不能處理。
于是,Linux下給了一個(gè)叫tcp_syncookies的參數(shù)來(lái)應(yīng)對(duì)這個(gè)事:當(dāng)SYN隊(duì)列滿了后,TCP會(huì)通過(guò)源地址端口、目標(biāo)地址端口和時(shí)間戳打造出一個(gè)特別的Sequence Number發(fā)回去(又叫cookie),此時(shí)服務(wù)器并沒(méi)有保留客戶端的SYN包。如果是攻擊者則不會(huì)有響應(yīng),如果是正常連接,則會(huì)把這個(gè)SYN Cookie發(fā)回來(lái),然后服務(wù)端可以通過(guò)cookie建連接(即使你不在SYN隊(duì)列中)。
千萬(wàn)別用tcp_syncookies來(lái)處理正常的大負(fù)載的連接的情況。因?yàn)閟ync cookies是妥協(xié)版的TCP協(xié)議,并不嚴(yán)謹(jǐn)。應(yīng)該調(diào)整三個(gè)TCP參數(shù):tcp_synack_retries減少重試次數(shù),tcp_max_syn_backlog增大SYN連接數(shù),tcp_abort_on_overflow處理不過(guò)來(lái)干脆就直接拒絕連接

斷開(kāi)連接

因?yàn)門CP是全雙工的,因此斷開(kāi)連接需要4次揮手,發(fā)送方和接收方都需要發(fā)送Fin和Ack。如果兩邊同時(shí)斷連接,那就會(huì)就進(jìn)入到CLOSING狀態(tài),然后到達(dá)TIME_WAIT狀態(tài)。

MSL

指的是報(bào)文段的最大生存時(shí)間,如果報(bào)文段在網(wǎng)絡(luò)中活動(dòng)了MSL時(shí)間,還沒(méi)有被接收,那么會(huì)被丟棄。關(guān)于MSL的大小,RFC 793協(xié)議中給出的建議是兩分鐘,不過(guò)實(shí)際上不同的操作系統(tǒng)可能有不同的設(shè)置,以Linux為例,通常是半分鐘,兩倍的MSL就是一分鐘,也就是60秒

TIME_WAIT狀態(tài)

主動(dòng)關(guān)閉的一方會(huì)進(jìn)入TIME_WAIT狀態(tài),并且在此狀態(tài)停留兩倍的MSL時(shí)長(zhǎng)。由于TIME_WAIT的存在,大量短連接會(huì)占有大量的端口,造成無(wú)法新建連接。

存在的意義

  1. TIME_WAIT確保有足夠的時(shí)間讓對(duì)端收到了ACK,如果被動(dòng)關(guān)閉的那方?jīng)]有收到Ack,就會(huì)觸發(fā)被動(dòng)端重發(fā)Fin,一來(lái)一去正好2個(gè)MSL
  2. 有足夠的時(shí)間讓這個(gè)連接不會(huì)跟后面的連接混在一起(有些自做主張的路由器會(huì)緩存IP數(shù)據(jù)包,如果連接被重用了,那么這些延遲收到的包就有可能會(huì)跟新連接混在一起)??梢钥纯催@篇文章《TIME_WAIT and its design implications for protocols and scalable client server systems》

解決辦法

  1. 盡量重用已有連接:不要頻繁地創(chuàng)建和關(guān)閉連接,例如啟用KeepAlive機(jī)制或者使用連接池機(jī)制
  2. 增大可用端口數(shù)
    sysctl -a | grep portnet.ipv4.ip_local_port_range = 32768 61000
    那么可用的端口數(shù)為 (61000-32768+1)=28223
    可通過(guò)設(shè)置sysctl net.ipv4.ip_local_port_range=10240 61000來(lái)增大可用端口的范圍
  3. 啟用SO_LINGER
    這個(gè)選項(xiàng)有點(diǎn)危險(xiǎn),會(huì)導(dǎo)致數(shù)據(jù)丟失。
    關(guān)閉選項(xiàng),close調(diào)用會(huì)立刻返回給調(diào)用者,如果發(fā)送緩沖區(qū)還有數(shù)據(jù),系統(tǒng)將繼續(xù)發(fā)送。這是默認(rèn)情況
    啟用選項(xiàng),并設(shè)置linger=0,TCP將丟棄保留在發(fā)送緩沖區(qū)的數(shù)據(jù)并發(fā)送一個(gè)RST給對(duì)方,而不是正常的4次揮手,從而避免了TIME_WAIT狀態(tài)。注意的是只有發(fā)送緩沖區(qū)有數(shù)據(jù)的情況下才發(fā)送RST,沒(méi)有數(shù)據(jù)的情況還是走正常流程。
    啟用選項(xiàng),并設(shè)置linger的值大于0,當(dāng)關(guān)閉連接時(shí),將延遲一段時(shí)間(linger的值)。如果發(fā)送緩沖區(qū)還有數(shù)據(jù),進(jìn)程將處于等待狀態(tài),直到1)所有數(shù)據(jù)都發(fā)送完且被對(duì)方確認(rèn),之后正常關(guān)閉2)延遲時(shí)間到,此時(shí)數(shù)據(jù)會(huì)被丟棄。
  4. 啟用tcp_tw_recyle,回收TIME_WAIT連接
    TCP有一種行為,可以緩存每個(gè)主機(jī)最新的時(shí)間戳,后續(xù)請(qǐng)求中如果時(shí)間戳小于緩存的時(shí)間戳,即視為無(wú)效,相應(yīng)的數(shù)據(jù)包會(huì)被丟棄。要啟用這種行為,要同時(shí)設(shè)置tcp_timestamps=1和tcp_tw_recycle=1,缺一不可。啟用后,60s內(nèi)同一源ip主機(jī)的socket connect請(qǐng)求中的timestamp必須是遞增的。單獨(dú)啟用tcp_tw_recycle,而關(guān)閉tcp_timestamps,是不起作用的。這個(gè)設(shè)置會(huì)導(dǎo)致一些問(wèn)題:當(dāng)多個(gè)客戶端通過(guò)NAT方式聯(lián)網(wǎng)并與服務(wù)端交互時(shí),服務(wù)端看到的是同一個(gè)IP,也就是說(shuō)對(duì)服務(wù)端而言這些客戶端實(shí)際上等同于一個(gè),可惜由于這些客戶端的時(shí)間戳可能存在差異,于是乎從服務(wù)端的視角看,便可能出現(xiàn)時(shí)間戳錯(cuò)亂的現(xiàn)象,進(jìn)而直接導(dǎo)致時(shí)間戳小的數(shù)據(jù)包被丟棄。具體的表現(xiàn)通常是有些客戶端連接成功,有些失敗。具體有多少被drop的包呢?使用netstat -s的其中一行7439 packets rejects in established connections because of timestamp
  5. 啟用tcp_tw_reuse,復(fù)用TIME_WAIT連接
    當(dāng)創(chuàng)建新連接的時(shí)候,如果可能的話會(huì)考慮復(fù)用相應(yīng)的TIME_WAIT連接
    1)TIME_WAIT創(chuàng)建時(shí)間必須超過(guò)一秒才可能會(huì)被復(fù)用
    2)只有連接的時(shí)間戳是遞增的時(shí)候才會(huì)被復(fù)用。
    如果使用tcp_tw_reuse,必需設(shè)置tcp_timestamps=1
    要復(fù)用連接,應(yīng)該在連接的發(fā)起方使用,而不能在被連接方使用。舉例來(lái)說(shuō):客戶端向服務(wù)端發(fā)起HTTP請(qǐng)求,服務(wù)端響應(yīng)后主動(dòng)關(guān)閉連接,于是TIME_WAIT便留在了服務(wù)端,此類情況使用「tcp_tw_reuse」是無(wú)效的,因?yàn)榉?wù)端是被連接方,所以不存在復(fù)用連接一說(shuō)。比如說(shuō)服務(wù)端是PHP,它查詢另一個(gè)MySQL服務(wù)端,然后主動(dòng)斷開(kāi)連接,于是TIME_WAIT就落在了PHP一側(cè),此類情況下使用「tcp_tw_reuse」是有效的
  6. 設(shè)置tcp_max_tw_buckets,控制TIME_WAIT總數(shù)
    默認(rèn)值是180000,如果超限,那么系統(tǒng)會(huì)把多的給destory掉,然后在日志里打一個(gè)警告(如:time wait bucket table overflow)官網(wǎng)文檔說(shuō)這個(gè)參數(shù)是用來(lái)對(duì)抗DDoS攻擊的,平常不要人為的降低它。

CLOSE_WAIT狀態(tài)

主動(dòng)關(guān)閉的一方發(fā)出 FIN包,被動(dòng)關(guān)閉的一方響應(yīng)ACK包,此時(shí),被動(dòng)關(guān)閉的一方就進(jìn)入了CLOSE_WAIT狀態(tài)。如果一切正常,稍后被動(dòng)關(guān)閉的一方也會(huì)發(fā)出FIN包,然后遷移到LAST_ACK狀態(tài)。

CLOSE_WAIT狀態(tài)在服務(wù)器停留時(shí)間很短,如果你發(fā)現(xiàn)大量的 CLOSE_WAIT狀態(tài),那么就意味著被動(dòng)關(guān)閉的一方?jīng)]有及時(shí)發(fā)出FIN包。

解決辦法

  1. 程序問(wèn)題:如果代碼層面忘記了close相應(yīng)的連接,那么自然不會(huì)發(fā)出FIN包,從而導(dǎo)致CLOSE_WAIT累積
  2. 響應(yīng)太慢或者超時(shí)設(shè)置過(guò)?。喝绻B接雙方不和諧,一方不耐煩直接 timeout,另一方卻還在忙于耗時(shí)邏輯,就會(huì)導(dǎo)致close被延后
  3. accept backlog太大:如果backlog太大的話,設(shè)想突然遭遇大訪問(wèn)量的話,即便響應(yīng)速度不慢,也可能出現(xiàn)來(lái)不及消費(fèi)的情況,導(dǎo)致多余的請(qǐng)求還在隊(duì)列里就被對(duì)方關(guān)閉了

重傳機(jī)制

TCP要保證所有的數(shù)據(jù)包都可以到達(dá),所以,必需要有重傳機(jī)制。

接收端給發(fā)送端的Ack確認(rèn)只會(huì)確認(rèn)最后一個(gè)連續(xù)的包,比如,發(fā)送端發(fā)了1,2,3,4,5一共五份數(shù)據(jù),接收端收到了1,2,于是回ack 3,然后收到了4(注意此時(shí)3沒(méi)收到),此時(shí)的TCP會(huì)怎么辦?我們要知道,因?yàn)檎缜懊嫠f(shuō)的,SeqNum和Ack是以字節(jié)數(shù)為單位,所以ack的時(shí)候,不能跳著確認(rèn),只能確認(rèn)最大的連續(xù)收到的包,不然,發(fā)送端就以為之前的都收到了

超時(shí)重傳機(jī)制

  • 一種是僅重傳timeout的包。也就是第3份數(shù)據(jù)。節(jié)省帶寬,但是慢(后面的也有可能是超時(shí)了)
  • 一種是重傳timeout后所有的數(shù)據(jù),也就是第3,4,5這三份數(shù)據(jù)。快一點(diǎn),但是會(huì)浪費(fèi)帶寬,也可能會(huì)有無(wú)用功

但總體來(lái)說(shuō)都不好。因?yàn)槎荚诘萾imeout,timeout可能會(huì)很長(zhǎng)

快速重傳機(jī)制

不以時(shí)間驅(qū)動(dòng),而以數(shù)據(jù)驅(qū)動(dòng)重傳
如果包沒(méi)有連續(xù)到達(dá),就ack最后那個(gè)可能被丟了的包,如果發(fā)送方連續(xù)收到3次相同的ack,就重傳

SACK方法

Selective Acknowledgment, 需要在TCP頭里加一個(gè)SACK的東西,ACK還是Fast Retransmit的ACK,SACK則是匯報(bào)收到的數(shù)據(jù)碎版,在發(fā)送端就可以根據(jù)回傳的SACK來(lái)知道哪些數(shù)據(jù)到了,哪些沒(méi)有收到

Duplicate SACK

重復(fù)收到數(shù)據(jù)的問(wèn)題,使用了SACK來(lái)告訴發(fā)送方有哪些數(shù)據(jù)被重復(fù)接收了

Timeout的設(shè)置

  • 設(shè)長(zhǎng)了,重發(fā)就慢,丟了老半天才重發(fā),沒(méi)有效率,性能差
  • 設(shè)短了,會(huì)導(dǎo)致可能并沒(méi)有丟就重發(fā)。于是重發(fā)的就快,會(huì)增加網(wǎng)絡(luò)擁塞,導(dǎo)致更多的超時(shí),更多的超時(shí)導(dǎo)致更多的重發(fā)

經(jīng)典算法:Karn/Partridge算法,Jacobson/Karels算法

流量控制

滑動(dòng)窗口

TCP必需要知道網(wǎng)絡(luò)實(shí)際的數(shù)據(jù)處理帶寬或是數(shù)據(jù)處理速度,這樣才不會(huì)引起網(wǎng)絡(luò)擁塞,導(dǎo)致丟包

Advertised-Window:接收端告訴發(fā)送端自己還有多少緩沖區(qū)可以接收數(shù)據(jù)。于是發(fā)送端就可以根據(jù)這個(gè)接收端的處理能力來(lái)發(fā)送數(shù)據(jù),而不會(huì)導(dǎo)致接收端處理不過(guò)來(lái)

接收端LastByteRead指向了TCP緩沖區(qū)中讀到的位置,NextByteExpected指向的地方是收到的連續(xù)包的最后一個(gè)位置,LastByteRcved指向的是收到的包的最后一個(gè)位置,我們可以看到中間有些數(shù)據(jù)還沒(méi)有到達(dá),所以有數(shù)據(jù)空白區(qū)。

發(fā)送端的LastByteAcked指向了被接收端Ack過(guò)的位置(表示成功發(fā)送確認(rèn)),LastByteSent表示發(fā)出去了,但還沒(méi)有收到成功確認(rèn)的Ack,LastByteWritten指向的是上層應(yīng)用正在寫(xiě)的地方。

接收端在給發(fā)送端回ACK中會(huì)匯報(bào)自己的AdvertisedWindow = MaxRcvBuffer – LastByteRcvd – 1;

  • 黑模型就是滑動(dòng)窗口
  • 1已收到ack確認(rèn)的數(shù)據(jù)
  • 2發(fā)還沒(méi)收到ack的
  • 3在窗口中還沒(méi)有發(fā)出的(接收方還有空間)
  • 4窗口以外的數(shù)據(jù)(接收方?jīng)]空間)

收到36的ack,并發(fā)出了46-51的字節(jié)

Zero Window

如果Window變成0了,發(fā)送端就不發(fā)數(shù)據(jù)了

如果發(fā)送端不發(fā)數(shù)據(jù)了,接收方一會(huì)兒Window size 可用了,怎么通知發(fā)送端呢:TCP使用了Zero Window Probe技術(shù),縮寫(xiě)為ZWP,也就是說(shuō),發(fā)送端在窗口變成0后,會(huì)發(fā)ZWP的包給接收方,讓接收方來(lái)ack他的Window尺寸,一般這個(gè)值會(huì)設(shè)置成3次,每次大約30-60秒。如果3次過(guò)后還是0的話,有的TCP實(shí)現(xiàn)就會(huì)發(fā)RST把鏈接斷了。

Silly Window Syndrome(糊涂窗口綜合癥)

如果你的網(wǎng)絡(luò)包可以塞滿MTU,那么你可以用滿整個(gè)帶寬,如果不能,那么你就會(huì)浪費(fèi)帶寬。避免對(duì)小的window size做出響應(yīng),直到有足夠大的window size再響應(yīng)。

如果這個(gè)問(wèn)題是由Receiver端引起的,那么就會(huì)使用David D Clark’s 方案。在receiver端,如果收到的數(shù)據(jù)導(dǎo)致window size小于某個(gè)值,可以直接ack(0)回sender,這樣就把window給關(guān)閉了,也阻止了sender再發(fā)數(shù)據(jù)過(guò)來(lái),等到receiver端處理了一些數(shù)據(jù)后windows size大于等于了MSS,或者receiver buffer有一半為空,就可以把window打開(kāi)讓send 發(fā)送數(shù)據(jù)過(guò)來(lái)。

如果這個(gè)問(wèn)題是由Sender端引起的,那么就會(huì)使用著名的 Nagle’s algorithm。這個(gè)算法的思路也是延時(shí)處理,他有兩個(gè)主要的條件:1)要等到 Window Size >= MSS 或是 Data Size >= MSS,2)等待時(shí)間或是超時(shí)200ms,這兩個(gè)條件有一個(gè)滿足,他才會(huì)發(fā)數(shù)據(jù),否則就是在攢數(shù)據(jù)。

TCP_CORK是禁止小包發(fā)送,而Nagle算法沒(méi)有禁止小包發(fā)送,只是禁止了大量的小包發(fā)送

擁塞控制

TCP不是一個(gè)自私的協(xié)議,當(dāng)擁塞發(fā)生的時(shí)候,要做自我犧牲

擁塞控制的論文請(qǐng)參看《Congestion Avoidance and Control》

主要算法有:慢啟動(dòng),擁塞避免,擁塞發(fā)生,快速恢復(fù),TCP New Reno,F(xiàn)ACK算法,TCP Vegas擁塞控制算法

參考

TCP網(wǎng)絡(luò)協(xié)議及其思想的應(yīng)用
TCP 的那些事兒(上)
TCP 的那些事兒(下)
tcp為什么是三次握手,為什么不是兩次或四次?
記一次TIME_WAIT網(wǎng)絡(luò)故障
再敘TIME_WAIT
tcp_tw_recycle和tcp_timestamps導(dǎo)致connect失敗問(wèn)題
tcp短連接TIME_WAIT問(wèn)題解決方法大全(1)- 高屋建瓴
tcp短連接TIME_WAIT問(wèn)題解決方法大全(2)- SO_LINGER
tcp短連接TIME_WAIT問(wèn)題解決方法大全(3)- tcp_tw_recycle
tcp短連接TIME_WAIT問(wèn)題解決方法大全(4)- tcp_tw_reuse
tcp短連接TIME_WAIT問(wèn)題解決方法大全(5)- tcp_max_tw_buckets
TCP的TIME_WAIT快速回收與重用
淺談CLOSE_WAIT
又見(jiàn)CLOSE_WAIT
PHP升級(jí)導(dǎo)致系統(tǒng)負(fù)載過(guò)高問(wèn)題分析
Coping with the TCP TIME-WAIT state on busy Linux servers

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、TCP狀態(tài)linux查看tcp的狀態(tài)命令:1)、netstat -nat 查看TCP各個(gè)狀態(tài)的數(shù)量2)、lso...
    北辰青閱讀 9,739評(píng)論 0 11
  • 個(gè)人認(rèn)為,Goodboy1881先生的TCP /IP 協(xié)議詳解學(xué)習(xí)博客系列博客是一部非常精彩的學(xué)習(xí)筆記,這雖然只是...
    貳零壹柒_fc10閱讀 5,215評(píng)論 0 8
  • 1.這篇文章不是本人原創(chuàng)的,只是個(gè)人為了對(duì)這部分知識(shí)做一個(gè)整理和系統(tǒng)的輸出而編輯成的,在此鄭重地向本文所引用文章的...
    SOMCENT閱讀 13,390評(píng)論 6 174
  • 最近在惡補(bǔ)計(jì)算機(jī)網(wǎng)絡(luò)方面的知識(shí),之前對(duì)于TCP的三次握手和四次分手也是模模糊糊,對(duì)于其中的細(xì)節(jié)更是渾然不知,最近看...
    微醺歲月閱讀 9,669評(píng)論 4 128
  • 套接字選項(xiàng)SO_RESUEADDR 即使端口處于2MSL狀態(tài),使用該選項(xiàng),仍然能夠在該端口建立連接。服務(wù)器常會(huì)設(shè)置...
    Myth52125閱讀 1,524評(píng)論 0 0

友情鏈接更多精彩內(nèi)容