TCP是一個巨復(fù)雜的協(xié)議劳秋,因為他要解決很多問題分尸,而這些問題又帶出了很多子問題和陰暗面。所以學(xué)習(xí)TCP本身是個比較痛苦的過程当编,但對于學(xué)習(xí)的過程卻能讓人有很多收獲。關(guān)于TCP這個協(xié)議的細(xì)節(jié)徒溪,我還是推薦你去看W.Richard Stevens的《TCP/IP 詳解 卷1:協(xié)議》(當(dāng)然忿偷,你也可以去讀一下RFC793以及后面N多的RFC)。另外臊泌,本文我會使用英文術(shù)語鲤桥,這樣方便你通過這些英文關(guān)鍵詞來查找相關(guān)的技術(shù)文檔。
之所以想寫這篇文章渠概,目的有三個茶凳,
- 一個是想鍛煉一下自己是否可以用簡單的篇幅把這么復(fù)雜的TCP協(xié)議描清楚的能力。
- 另一個是覺得現(xiàn)在的好多程序員基本上不會認(rèn)認(rèn)真真地讀本書播揪,喜歡快餐文化贮喧,所以,希望這篇快餐文章可以讓你對TCP這個古典技術(shù)有所了解猪狈,并能體會到軟件設(shè)計中的種種難處箱沦。并且你可以從中有一些軟件設(shè)計上的收獲。
- 最重要的希望這些基礎(chǔ)知識可以讓你搞清很多以前一些似是而非的東西雇庙,并且你能意識到基礎(chǔ)的重要谓形。
所以,本文不會面面俱到状共,只是對TCP協(xié)議套耕、算法和原理的科普。
我本來只想寫一個篇幅的文章的峡继,但是TCP真TMD的復(fù)雜冯袍,比C++復(fù)雜多了,這30多年來碾牌,各種優(yōu)化變種爭論和修改康愤。所以,寫著寫著就發(fā)現(xiàn)只有砍成兩篇舶吗。
廢話少說肴捉,首先,我們需要知道TCP在網(wǎng)絡(luò)OSI的七層模型中的第四層——Transport層
叔收,IP在第三層——Network層
齿穗,ARP在第二層——Data Link層
,在第二層上的數(shù)據(jù)饺律,我們叫Frame
窃页,在第三層上的數(shù)據(jù)叫Packet
,第四層的數(shù)據(jù)叫Segment
复濒。
首先脖卖,我們需要知道,我們程序的數(shù)據(jù)首先會打到TCP的Segment
中巧颈,然后TCP的Segment
會打到IP的Packet
中畦木,然后再打到以太網(wǎng)Ethernet
的Frame
中,傳到對端后洛二,各個層解析自己的協(xié)議馋劈,然后把數(shù)據(jù)交給更高層的協(xié)議處理。
TCP頭格式
接下來晾嘶,我們來看一下TCP頭的格式
你需要注意這么幾點:
- TCP的包是沒有IP地址的妓雾,那是IP層上的事。但是有源端口和目標(biāo)端口垒迂。
- 一個TCP連接需要四個元組來表示是同一個連接
(src_ip, src_port, dst_ip, dst_port)
準(zhǔn)確說是五元組械姻,還有一個是協(xié)議。但因為這里只是說TCP協(xié)議机断,所以楷拳,這里我只說四元組。 - 注意上圖中的四個非常重要的東西:
- Sequence Number是包的序號吏奸,用來解決網(wǎng)絡(luò)包亂序(reordering)問題欢揖。
- Acknowledgement Number就是ACK——用于確認(rèn)收到,用來解決不丟包的問題奋蔚。
- Window又叫Advertised-Window她混,也就是著名的滑動窗口(Sliding Window),用于解決流控的泊碑。
- TCP Flag 坤按,也就是包的類型,主要是用于操控TCP的狀態(tài)機的馒过。
關(guān)于其它的東西臭脓,可以參看下面的圖示
TCP的狀態(tài)機
其實,網(wǎng)絡(luò)上的傳輸是沒有連接的腹忽,包括TCP也是一樣的来累。而TCP所謂的“連接”砚作,其實只不過是在通訊的雙方維護(hù)一個“連接狀態(tài)”,讓它看上去好像有連接一樣佃扼。所以偎巢,TCP的狀態(tài)變換是非常重要的。
下面是:“TCP協(xié)議的狀態(tài)機”(圖片來源) 和 “TCP建鏈接”兼耀、“TCP斷鏈接”、“傳數(shù)據(jù)” 的對照圖求冷,我把兩個圖并排放在一起瘤运,這樣方便在你對照著看。另外匠题,下面這兩個圖非常非常的重要拯坟,你一定要記牢。(吐個槽:看到這樣復(fù)雜的狀態(tài)機韭山,就知道這個協(xié)議有多復(fù)雜郁季,復(fù)雜的東西總是有很多坑爹的事情,所以TCP協(xié)議其實也挺坑爹的)
很多人會問钱磅,為什么建鏈接要3次握手梦裂,斷鏈接需要4次揮手?
對于建鏈接的3次握手盖淡,主要是要初始化Sequence Number 的初始值年柠。通信的雙方要互相通知對方自己的初始化的Sequence Number(縮寫為ISN:Inital Sequence Number)——所以叫SYN,全稱Synchronize Sequence Numbers褪迟。也就上圖中的 x 和 y冗恨。這個號要作為以后的數(shù)據(jù)通信的序號,以保證應(yīng)用層接收到的數(shù)據(jù)不會因為網(wǎng)絡(luò)上的傳輸?shù)膯栴}而亂序(TCP會用這個序號來拼接數(shù)據(jù))味赃。
-
對于4次揮手掀抹,其實你仔細(xì)看是2次,因為TCP是全雙工的心俗,所以傲武,發(fā)送方和接收方都需要Fin和Ack。只不過另凌,有一方是被動的谱轨,所以看上去就成了所謂的4次揮手。如果兩邊同時斷連接吠谢,那就會就進(jìn)入到CLOSING狀態(tài)土童,然后到達(dá)TIME_WAIT狀態(tài)。下圖是雙方同時斷連接的示意圖(你同樣可以對照著TCP狀態(tài)機看):
兩端同時斷連接
另外工坊,有幾個事情需要注意一下:
關(guān)于建連接時SYN超時献汗。試想一下敢订,如果server端接到了clien發(fā)的SYN后回了SYN-ACK后client掉線了,server端沒有收到client回來的ACK罢吃,那么楚午,這個連接處于一個中間狀態(tài),即沒成功尿招,也沒失敗矾柜。于是,server端如果在一定時間內(nèi)沒有收到的TCP會重發(fā)SYN-ACK就谜。在Linux下怪蔑,默認(rèn)重試次數(shù)為5次,重試的間隔時間從1s開始每次都翻售丧荐,5次的重試時間間隔為1s, 2s, 4s, 8s, 16s缆瓣,總共31s,第5次發(fā)出后還要等32s都知道第5次也超時了虹统,所以弓坞,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,TCP才會把斷開這個連接车荔。
關(guān)于SYN Flood攻擊渡冻。一些惡意的人就為此制造了SYN Flood攻擊——給服務(wù)器發(fā)了一個SYN后,就下線了夸赫,于是服務(wù)器需要默認(rèn)等63s才會斷開連接菩帝,這樣,攻擊者就可以把服務(wù)器的syn連接的隊列耗盡茬腿,讓正常的連接請求不能處理呼奢。于是,Linux下給了一個叫tcp_syncookies的參數(shù)來應(yīng)對這個事——當(dāng)SYN隊列滿了后切平,TCP會通過源地址端口握础、目標(biāo)地址端口和時間戳打造出一個特別的Sequence Number發(fā)回去(又叫cookie),如果是攻擊者則不會有響應(yīng)悴品,如果是正常連接禀综,則會把這個 SYN Cookie發(fā)回來,然后服務(wù)端可以通過cookie建連接(即使你不在SYN隊列中)苔严。請注意定枷,請先千萬別用tcp_syncookies來處理正常的大負(fù)載的連接的情況。因為届氢,synccookies是妥協(xié)版的TCP協(xié)議欠窒,并不嚴(yán)謹(jǐn)。對于正常的請求退子,你應(yīng)該調(diào)整三個TCP參數(shù)可供你選擇岖妄,第一個是:tcp_synack_retries 可以用他來減少重試次數(shù)型将;第二個是:tcp_max_syn_backlog,可以增大SYN連接數(shù)荐虐;第三個是:tcp_abort_on_overflow 處理不過來干脆就直接拒絕連接了七兜。
關(guān)于ISN的初始化。ISN是不能hard code的福扬,不然會出問題的——比如:如果連接建好后始終用1來做ISN腕铸,如果client發(fā)了30個segment過去,但是網(wǎng)絡(luò)斷了忧换,于是 client重連恬惯,又用了1做ISN,但是之前連接的那些包到了亚茬,于是就被當(dāng)成了新連接的包,此時浓恳,client的Sequence Number 可能是3刹缝,而Server端認(rèn)為client端的這個號是30了。全亂了颈将。RFC793中說梢夯,ISN會和一個假的時鐘綁在一起,這個時鐘會在每4微秒對ISN做加一操作晴圾,直到超過2^32颂砸,又從0開始。這樣死姚,一個ISN的周期大約是4.55個小時人乓。因為,我們假設(shè)我們的TCP Segment在網(wǎng)絡(luò)上的存活時間不會超過Maximum Segment Lifetime(縮寫為MSL – Wikipedia語條)都毒,所以色罚,只要MSL的值小于4.55小時,那么账劲,我們就不會重用到ISN戳护。
關(guān)于 MSL 和 TIME_WAIT。通過上面的ISN的描述瀑焦,相信你也知道MSL是怎么來的了腌且。我們注意到,在TCP的狀態(tài)圖中榛瓮,從TIME_WAIT狀態(tài)到CLOSED狀態(tài)铺董,有一個超時設(shè)置,這個超時設(shè)置是 2*MSL(RFC793定義了MSL為2分鐘榆芦,Linux設(shè)置成了30s)為什么要這有TIME_WAIT柄粹?為什么不直接給轉(zhuǎn)成CLOSED狀態(tài)呢喘鸟?主要有兩個原因:1)TIME_WAIT確保有足夠的時間讓對端收到了ACK,如果被動關(guān)閉的那方?jīng)]有收到Ack驻右,就會觸發(fā)被動端重發(fā)Fin什黑,一來一去正好2個MSL,2)有足夠的時間讓這個連接不會跟后面的連接混在一起(你要知道堪夭,有些自做主張的路由器會緩存IP數(shù)據(jù)包愕把,如果連接被重用了,那么這些延遲收到的包就有可能會跟新連接混在一起)森爽。你可以看看這篇文章《TIME_WAIT and its design implications for protocols and scalable client server systems》
關(guān)于TIME_WAIT數(shù)量太多恨豁。從上面的描述我們可以知道,TIME_WAIT是個很重要的狀態(tài)爬迟,但是如果在大并發(fā)的短鏈接下橘蜜,TIME_WAIT 就會太多,這也會消耗很多系統(tǒng)資源付呕。只要搜一下计福,你就會發(fā)現(xiàn),十有八九的處理方式都是教你設(shè)置兩個參數(shù)徽职,一個叫tcp_tw_reuse象颖,另一個叫tcp_tw_recycle的參數(shù),這兩個參數(shù)默認(rèn)值都是被關(guān)閉的姆钉,后者recyle比前者resue更為激進(jìn)说订,resue要溫柔一些。另外潮瓶,如果使用tcp_tw_reuse陶冷,必需設(shè)置tcp_timestamps=1,否則無效筋讨。這里埃叭,你一定要注意,打開這兩個參數(shù)會有比較大的坑——可能會讓TCP連接出一些詭異的問題(因為如上述一樣悉罕,如果不等待超時重用連接的話赤屋,新的連接可能會建不上。正如官方文檔上說的一樣“It should not be changed without advice/request of technical experts”)壁袄。
關(guān)于tcp_tw_reuse类早。官方文檔上說tcp_tw_reuse 加上tcp_timestamps(又叫PAWS, for Protection Against Wrapped Sequence Numbers)可以保證協(xié)議的角度上的安全,但是你需要tcp_timestamps在兩邊都被打開(你可以讀一下tcp_twsk_unique的源碼 )嗜逻。我個人估計還是有一些場景會有問題恋捆。
關(guān)于tcp_tw_recycle券册。如果是tcp_tw_recycle被打開了話撞鹉,會假設(shè)對端開啟了tcp_timestamps,然后會去比較時間戳嵌巷,如果時間戳變大了,就可以重用室抽。但是搪哪,如果對端是一個NAT網(wǎng)絡(luò)的話(如:一個公司只用一個IP出公網(wǎng))或是對端的IP被另一臺重用了,這個事就復(fù)雜了坪圾。建鏈接的SYN可能就被直接丟掉了(你可能會看到connection time out的錯誤)(如果你想觀摩一下Linux的內(nèi)核代碼晓折,請參看源碼 tcp_timewait_state_process)。
關(guān)于tcp_max_tw_buckets兽泄。這個是控制并發(fā)的TIME_WAIT的數(shù)量漓概,默認(rèn)值是180000,如果超限病梢,那么胃珍,系統(tǒng)會把多的給destory掉,然后在日志里打一個警告(如:time wait bucket table overflow)蜓陌,官網(wǎng)文檔說這個參數(shù)是用來對抗DDoS攻擊的堂鲜。也說的默認(rèn)值180000并不小。這個還是需要根據(jù)實際情況考慮护奈。
**Again,使用tcp_tw_reuse和tcp_tw_recycle來解決TIME_WAIT的問題是非常非常危險的哥纫,因為這兩個參數(shù)違反了TCP協(xié)議(RFC 1122) **
其實霉旗,TIME_WAIT表示的是你主動斷連接,所以蛀骇,這就是所謂的“不作死不會死”厌秒。試想,如果讓對端斷連接擅憔,那么這個破問題就是對方的了鸵闪,呵呵。另外暑诸,如果你的服務(wù)器是于HTTP服務(wù)器蚌讼,那么設(shè)置一個HTTP的KeepAlive有多重要(瀏覽器會重用一個TCP連接來處理多個HTTP請求),然后讓客戶端去斷鏈接(你要小心个榕,瀏覽器可能會非常貪婪篡石,他們不到萬不得已不會主動斷連接)。
數(shù)據(jù)傳輸中的Sequence Number
下圖是我從Wireshark中截了個我在訪問coolshell.cn時的有數(shù)據(jù)傳輸?shù)膱D給你看一下西采,SeqNum是怎么變的凰萨。(使用Wireshark菜單中的Statistics ->Flow Graph… )
你可以看到,SeqNum的增加是和傳輸?shù)淖止?jié)數(shù)相關(guān)的。上圖中胖眷,三次握手后武通,來了兩個Len:1440的包,而第二個包的SeqNum就成了1441珊搀。然后第一個ACK回的是1441冶忱,表示第一個1440收到了。
注意:如果你用Wireshark抓包程序看3次握手食棕,你會發(fā)現(xiàn)SeqNum總是為0朗和,不是這樣的,Wireshark為了顯示更友好簿晓,使用了Relative SeqNum——相對序號眶拉,你只要在右鍵菜單中的protocol preference 中取消掉就可以看到“Absolute SeqNum”了
TCP重傳機制
TCP要保證所有的數(shù)據(jù)包都可以到達(dá),所以憔儿,必需要有重傳機制忆植。
注意,接收端給發(fā)送端的Ack確認(rèn)只會確認(rèn)最后一個連續(xù)的包谒臼,比如朝刊,發(fā)送端發(fā)了1,2,3,4,5一共五份數(shù)據(jù),接收端收到了1蜈缤,2拾氓,于是回ack 3,然后收到了4(注意此時3沒收到)底哥,此時的TCP會怎么辦咙鞍?我們要知道,因為正如前面所說的趾徽,SeqNum和Ack是以字節(jié)數(shù)為單位续滋,所以ack的時候,不能跳著確認(rèn)孵奶,只能確認(rèn)最大的連續(xù)收到的包疲酌,不然,發(fā)送端就以為之前的都收到了了袁。
超時重傳機制
一種是不回ack朗恳,死等3,當(dāng)發(fā)送方發(fā)現(xiàn)收不到3的ack超時后早像,會重傳3僻肖。一旦接收方收到3后,會ack 回 4——意味著3和4都收到了卢鹦。
但是臀脏,這種方式會有比較嚴(yán)重的問題劝堪,那就是因為要死等3,所以會導(dǎo)致4和5即便已經(jīng)收到了揉稚,而發(fā)送方也完全不知道發(fā)生了什么事秒啦,因為沒有收到Ack,所以搀玖,發(fā)送方可能會悲觀地認(rèn)為也丟了余境,所以有可能也會導(dǎo)致4和5的重傳。
對此有兩種選擇:
- 一種是僅重傳timeout的包灌诅。也就是第3份數(shù)據(jù)芳来。
- 另一種是重傳timeout后所有的數(shù)據(jù),也就是第3猜拾,4即舌,5這三份數(shù)據(jù)。
這兩種方式有好也有不好挎袜。第一種會節(jié)省帶寬顽聂,但是慢,第二種會快一點盯仪,但是會浪費帶寬紊搪,也可能會有無用功。但總體來說都不好全景。因為都在等timeout耀石,timeout可能會很長(在下篇會說TCP是怎么動態(tài)地計算出timeout的)
快速重傳機制
于是,TCP引入了一種叫Fast Retransmit 的算法爸黄,不以時間驅(qū)動娶牌,而以數(shù)據(jù)驅(qū)動重傳。也就是說馆纳,如果,包沒有連續(xù)到達(dá)汹桦,就ack最后那個可能被丟了的包鲁驶,如果發(fā)送方連續(xù)收到3次相同的ack,就重傳舞骆。Fast Retransmit的好處是不用等timeout了再重傳钥弯。
比如:如果發(fā)送方發(fā)出了1,2督禽,3脆霎,4,5份數(shù)據(jù)狈惫,第一份先到送了睛蛛,于是就ack回2,結(jié)果2因為某些原因沒收到,3到達(dá)了忆肾,于是還是ack回2荸频,后面的4和5都到了,但是還是ack回2客冈,因為2還是沒有收到旭从,于是發(fā)送端收到了三個ack=2的確認(rèn),知道了2還沒有到场仲,于是就馬上重轉(zhuǎn)2和悦。然后,接收端收到了2渠缕,此時因為3鸽素,4,5都收到了褐健,于是ack回6付鹿。示意圖如下:
Fast Retransmit只解決了一個問題,就是timeout的問題蚜迅,它依然面臨一個艱難的選擇舵匾,就是,是重傳之前的一個還是重傳所有的問題谁不。對于上面的示例來說坐梯,是重傳#2呢還是重傳#2,#3刹帕,#4吵血,#5呢?因為發(fā)送端并不清楚這連續(xù)的3個ack(2)是誰傳回來的偷溺?也許發(fā)送端發(fā)了20份數(shù)據(jù)蹋辅,是#6,#10挫掏,#20傳來的呢侦另。這樣,發(fā)送端很有可能要重傳從2到20的這堆數(shù)據(jù)(這就是某些TCP的實際的實現(xiàn))尉共“担可見,這是一把雙刃劍袄友。
SACK 方法
另外一種更好的方式叫:Selective Acknowledgment (SACK)(參看RFC 2018)殿托,這種方式需要在TCP頭里加一個SACK的東西,ACK還是Fast Retransmit的ACK剧蚣,SACK則是匯報收到的數(shù)據(jù)碎版支竹。參看下圖:
這樣旋廷,在發(fā)送端就可以根據(jù)回傳的SACK來知道哪些數(shù)據(jù)到了,哪些沒有到唾戚。于是就優(yōu)化了Fast Retransmit的算法柳洋。當(dāng)然,這個協(xié)議需要兩邊都支持叹坦。在 Linux下熊镣,可以通過tcp_sack參數(shù)打開這個功能(Linux 2.4后默認(rèn)打開)。
這里還需要注意一個問題——接收方Reneging募书,所謂Reneging的意思就是接收方有權(quán)把已經(jīng)報給發(fā)送端SACK里的數(shù)據(jù)給丟了绪囱。這樣干是不被鼓勵的,因為這個事會把問題復(fù)雜化了莹捡,但是鬼吵,接收方這么做可能會有些極端情況,比如要把內(nèi)存給別的更重要的東西篮赢。所以齿椅,發(fā)送方也不能完全依賴SACK,還是要依賴ACK启泣,并維護(hù)Time-Out涣脚,如果后續(xù)的ACK沒有增長,那么還是要把SACK的東西重傳寥茫,另外遣蚀,接收端這邊永遠(yuǎn)不能把SACK的包標(biāo)記為Ack。
注意:SACK會消費發(fā)送方的資源纱耻,試想芭梯,如果一個攻擊者給數(shù)據(jù)發(fā)送方發(fā)一堆SACK的選項,這會導(dǎo)致發(fā)送方開始要重傳甚至遍歷已經(jīng)發(fā)出的數(shù)據(jù)弄喘,這會消耗很多發(fā)送端的資源玖喘。詳細(xì)的東西請參看《TCP SACK的性能權(quán)衡》
Duplicate SACK – 重復(fù)收到數(shù)據(jù)的問題
Duplicate SACK又稱D-SACK,其主要使用了SACK來告訴發(fā)送方有哪些數(shù)據(jù)被重復(fù)接收了蘑志。RFC-2883 里有詳細(xì)描述和示例芒涡。下面舉幾個例子(來源于RFC-2883)
D-SACK使用了SACK的第一個段來做標(biāo)志,
如果SACK的第一個段的范圍被ACK所覆蓋卖漫,那么就是D-SACK
如果SACK的第一個段的范圍被SACK的第二個段覆蓋,那么就是D-SACK
示例一:ACK丟包
下面的示例中赠群,丟了兩個ACK羊始,所以,發(fā)送端重傳了第一個數(shù)據(jù)包(3000-3499)查描,于是接收端發(fā)現(xiàn)重復(fù)收到突委,于是回了一個SACK=3000-3500柏卤,因為ACK都到了4000意味著收到了4000之前的所有數(shù)據(jù),所以這個SACK就是D-SACK——旨在告訴發(fā)送端我收到了重復(fù)的數(shù)據(jù)匀油,而且我們的發(fā)送端還知道缘缚,數(shù)據(jù)包沒有丟,丟的是ACK包敌蚜。
Transmitted Received ACK Sent
Segment Segment (Including SACK Blocks)
3000-3499 3000-3499 3500 (ACK dropped)
3500-3999 3500-3999 4000 (ACK dropped)
3000-3499 3000-3499 4000, SACK=3000-3500
** 示例二桥滨,網(wǎng)絡(luò)延誤**
下面的示例中,網(wǎng)絡(luò)包(1000-1499)被網(wǎng)絡(luò)給延誤了弛车,導(dǎo)致發(fā)送方?jīng)]有收到ACK齐媒,而后面到達(dá)的三個包觸發(fā)了“Fast Retransmit算法”,所以重傳纷跛,但重傳時喻括,被延誤的包又到了,所以贫奠,回了一個SACK=1000-1500唬血,因為ACK已到了3000,所以唤崭,這個SACK是D-SACK——標(biāo)識收到了重復(fù)的包拷恨。
這個案例下,發(fā)送端知道之前因為“Fast Retransmit算法”觸發(fā)的重傳不是因為發(fā)出去的包丟了浩姥,也不是因為回應(yīng)的ACK包丟了挑随,而是因為網(wǎng)絡(luò)延時了。
Transmitted Received ACK Sent
Segment Segment (Including SACK Blocks)
500-999 500-999 1000
1000-1499 (delayed)
1500-1999 1500-1999 1000, SACK=1500-2000
2000-2499 2000-2499 1000, SACK=1500-2500
2500-2999 2500-2999 1000, SACK=1500-3000
1000-1499 1000-1499 3000
1000-1499 3000, SACK=1000-1500
可見勒叠,引入了D-SACK兜挨,有這么幾個好處:
1)可以讓發(fā)送方知道,是發(fā)出去的包丟了眯分,還是回來的ACK包丟了拌汇。
2)是不是自己的timeout太小了,導(dǎo)致重傳弊决。
3)網(wǎng)絡(luò)上出現(xiàn)了先發(fā)的包后到的情況(又稱reordering)
4)網(wǎng)絡(luò)上是不是把我的數(shù)據(jù)包給復(fù)制了噪舀。
知道這些東西可以很好得幫助TCP了解網(wǎng)絡(luò)情況,從而可以更好的做網(wǎng)絡(luò)上的流控飘诗。
Linux下的tcp_dsack參數(shù)用于開啟這個功能(Linux 2.4后默認(rèn)打開)
好了与倡,上篇就到這里結(jié)束了。如果你覺得我寫得還比較淺顯易懂昆稿,那么纺座,歡迎移步看下篇《TCP的流迭、擁塞處理》