TCP的滑動窗口是一個很重要的概念,也是很晦澀的一個知識點。下面就大概介紹下TCP滑動窗口為什么出現(xiàn)?它是怎么工作的的?
什么是TCP窗口
首先,要理解,client和server各自協(xié)議棧都有自己的buffer,應(yīng)用層讀寫數(shù)據(jù)的源都是協(xié)議棧buffer里。以接收端為例,應(yīng)用程序調(diào)用read()時,會從buffer里移走數(shù)據(jù)到用戶空間,應(yīng)用程序讀的速度越快(read(1024)必然比read(1)要快),那么buffer里的內(nèi)容消費的越快,buffer也會越空。那么TCP就可以告訴client,我現(xiàn)在很閑,你可以發(fā)送更多的數(shù)據(jù)來。"更多"是多少?這就說窗口,窗口就是量化接收端和服務(wù)端當(dāng)前能處理數(shù)據(jù)的能力。
TCP窗口是如何工作的
client和server端建立連接后,client會告訴server,自己的"接收窗口"大小(自己能接收多少的數(shù)據(jù),受上面所說的buffer影響),server端接收到client的"接收窗口"大小,就會變成server端自己的"發(fā)送窗口"大小。同樣的,server端告訴client自己的"接收窗口"大小,就會變成客戶端的"發(fā)送窗口"大小。
為了理解TCP的窗口大小是怎么樣變化的,我們先需要理解它的含義。最簡單的方式就是認(rèn)為窗口大小"意味著接收方能接收數(shù)據(jù)的大小",這也是說接收端設(shè)備再應(yīng)用程序讀取buffer中數(shù)據(jù)之前,能從對端連接處理多少數(shù)據(jù)。比如說server端窗口大小是360,那么就意味著server端一次只能從客戶端接收不超過360bytes的數(shù)據(jù)。當(dāng)server端收到數(shù)據(jù),它會將數(shù)據(jù)放到buffer里,然后server端必須對這份數(shù)據(jù)做兩件事
1. server端必須發(fā)送一個 ACK 到client端來確認(rèn)數(shù)據(jù)已經(jīng)收到
2. server端必須處理這份數(shù)據(jù),把它交給對應(yīng)的應(yīng)用程序
要區(qū)分上面兩件事對理解窗口很重要,接收方收到數(shù)據(jù)后會確認(rèn),但是數(shù)據(jù)并不一定是里面就是從buffer里取出的,這是受應(yīng)用層邏輯控制的。所以很有可能如果接收數(shù)據(jù)過快,而取出數(shù)據(jù)更慢,就會導(dǎo)致buffer滿。一旦這種情況發(fā)生,窗口大小就開始調(diào)整來防止接收方負(fù)載過高。
正是因為窗口大小的調(diào)整可以用來調(diào)節(jié)數(shù)據(jù)傳輸?shù)乃俾?,所以就可以實現(xiàn)TCP的流控,在傳輸層的流控就是典型的例子,流控對于TCP的通信是很重要的,通過增大或者減小窗口的大小,client和server各自確保彼此設(shè)備發(fā)數(shù)據(jù)和收數(shù)據(jù)平衡。
通過TCP窗口實現(xiàn)流控
下面舉一個例子,來看TCP窗口大小變化怎樣實現(xiàn)流控。client端和server端已經(jīng)三次握手建立TCP連接,總窗口大小是TCP建立連接時候確定的。黑色框代表client和server總的窗口大小,紅色框代表實際可用的窗口大小。初始化的時候默認(rèn)client和server總窗口和可用端口分別都是360。另外,假設(shè)Client總共只發(fā)送360bytes數(shù)據(jù),所以總窗口大小不會往前移動。

client 發(fā)送140bytes到server端,Seq=1,Length=140;可用窗口大小往前移動,變成260bytes,總窗口大小不變,依然是360。這中間的120是已發(fā)送,等待確認(rèn);
server端收到140bytes,放入buffer中,但是應(yīng)用程序很繁忙,只取出100個字節(jié)。這時候可用窗口大小: 260(360-100)。接著server端要給client發(fā)確認(rèn),Ack=141,Window=260。黑框左邊緣向前移動,表示140字節(jié)已經(jīng)確認(rèn)收到,但是應(yīng)用程序太慢,處理很忙導(dǎo)致我現(xiàn)在只能處理260bytes(回顧下上面server端收到數(shù)據(jù)要做的兩件事);
client收到來自server端的ack回應(yīng),首先總窗口左邊緣向前移動,表示第一步的140bytes server已經(jīng)收到,剩下260數(shù)據(jù)。接著被告知server的可接收窗口是260,client就調(diào)整自己的發(fā)送窗口是260,表示一次發(fā)數(shù)據(jù)不能超過260;
client發(fā)送180bytes,可用窗口變成80(260-180),等待確認(rèn)發(fā)送的180bytes;
server收到180bytes,放入buffer中,應(yīng)用程序依然很繁忙,這次一個字節(jié)都不處理。此時可用窗口大小:80(260-180),然后發(fā)180 ack給client,并告知窗口大小80;
client收到ack,確認(rèn)之前發(fā)送的180已經(jīng)到達(dá),剩余數(shù)據(jù)還有80字節(jié)。被告知server端接收窗口大小是80,調(diào)整自己的發(fā)送窗口大小為80
client發(fā)送80bytes,可用窗口變成0(80-80),等待確認(rèn)發(fā)送的80bytes;
server收到180bytes,放入buffer中,應(yīng)用程序依然很繁忙,一個字節(jié)都不處理。此時可用窗口大小:80(80-80),然后發(fā)80 ack給client,并告知窗口大小0;
client收到ack,確認(rèn)之前發(fā)送的80已經(jīng)到達(dá)。被告知server端接收窗口大小是0,調(diào)整自己的發(fā)送窗口大小為0,此時無論client是否還有數(shù)據(jù)要發(fā)送,都不能再發(fā)了。
總結(jié)
窗口就是量化接收端和服務(wù)端當(dāng)前能處理數(shù)據(jù)的能力。個人理解,如發(fā)現(xiàn)接收端的窗口越來越小,或者越來越大,都可能會有問題。
1. 接收端的窗口越來越小,那么就是接收端處理不過來(1.發(fā)的程序太快,太多;2.收的程序太慢,太少;3.發(fā)端帶寬比收端帶寬更大),會導(dǎo)致接收端發(fā)送給發(fā)送端的窗口變小,從而發(fā)送端調(diào)整發(fā)送窗口大小,降低發(fā)送速度,網(wǎng)絡(luò)流量就會下降。
2. 接收端的窗口越來越大,那么就是發(fā)送端處理不過來(1.收的程序太快,太多;2.發(fā)的程序太慢,太少;3.發(fā)端帶寬比收端帶寬更小),會導(dǎo)致接收端發(fā)送給發(fā)送端的窗口變大,從而發(fā)送端調(diào)整發(fā)送窗口大小,增加發(fā)送速度,網(wǎng)絡(luò)流量就會升高。