|
TCP發(fā)送數(shù)據(jù)的時候并不是直接送出數(shù)據(jù)的,而是考慮了后續(xù)的數(shù)據(jù)一起發(fā)送所以,采用的是緩存隊列機制。
一、API參數(shù)
1、pcb指針
控制塊記錄這個連接的幾乎所有的例如端口、IP地址、發(fā)送隊列、接收窗口、序號等等自然不用細說。作為一個全局結(jié)構(gòu)記錄著你開的TCP所有的信息。所以只要找到他就可以知道和當(dāng)前連接的所有有關(guān)結(jié)構(gòu)的值。因此在程序調(diào)試過程中可以利用他來鎖定相關(guān)的參數(shù)值等。所以這個指針就是指向了當(dāng)前建立的連接。
2、 arg指針
顯然這個是個萬能的運載指針,這個是用來指向要發(fā)送的數(shù)據(jù)的,數(shù)據(jù)有兩種一種是駐留在內(nèi)存中也就是RAM中,另一種是駐留在FLASH中,也就是ROM中,在對待這兩者最大區(qū)別是pbuf的分配,如果駐留在內(nèi)存中,那么將會在RAM中開辟一塊數(shù)據(jù)區(qū)并把arg指針指向的數(shù)據(jù)搬到這塊內(nèi)存中,前者不是協(xié)議棧管理而是用戶的內(nèi)存區(qū),后者是協(xié)議棧管理的,用戶無權(quán)操作。如果是ROM那么好了PBUF只有個頭而已,并沒有分配實際的數(shù)據(jù)區(qū),他的數(shù)據(jù)區(qū)直接引用FLASH的地址,也就是一般const修飾的,編譯器會把讓他放入FLASH中去。
3、len
數(shù)據(jù)長度。要發(fā)送的數(shù)據(jù)有多么的長!請告訴他。
4、 apiflags
復(fù)制(RAM/ROM)和PSH標志
二、實現(xiàn)
1、如上參數(shù)傳進來之后系統(tǒng)先要檢查是否具備發(fā)送的條件:
1)、當(dāng)前TCP狀態(tài)是否允許做發(fā)送數(shù)據(jù)的動作?
2)、發(fā)送緩沖區(qū)的長度是都還夠用?
3)、發(fā)送隊列是否超出最大的定義長度?
如果全部通過則繼續(xù),否則自然不必說,返回錯誤代碼給應(yīng)用程序。
2、
queuelen = pcb->snd_queuelen
取出當(dāng)前發(fā)送隊列記錄里面的隊列長度,、
3、找出來未發(fā)送隊列中是否空?這里只討論空的情況,也就是類似在第一次發(fā)送的情況。
所以 pcb->unsent = NULL是成立的。
4、判斷是否copy應(yīng)用數(shù)據(jù)到pbuf中。也就是說數(shù)據(jù)位置在在RAM還是ROM中?
如果RAM中
開始從RAM中分配一片承載len的pbuf。然后 do memcpy
如果是ROM中
那么引用指針到描述符中就OK無須copy。
然后在RAM中分配一個頭部,然后連接起來
也就是頭部+數(shù)據(jù)(指向ROM中)的結(jié)構(gòu)。
5、queuelen += pbuf_clen(p)
計算出來pbuf數(shù)量。由于鏈表的結(jié)構(gòu)所以實現(xiàn)超級簡單 while (p != NULL) {
++len;
p = p->next;
}
這樣 queuelen 就是pbuf的數(shù)量了也就是p->next的數(shù)量,通常是RAMp->next=NULL就是 ++len=1了。
6、建立segment
動態(tài)從RAM中分配一塊MEMP_TCP_SEG 長度的segment,
然后就是對這個TCP段進行填充了,比如遠端的端口本地的端口序號、pbuf位置等。重要的是TCP的序號。也會被填入此種段域中記錄。端口這些全部來自pcb中。
7、將段域?qū)懭雙cb的usent隊列中去。
queue = seg;
pcb->unsent = queue;
8、更新PCB相關(guān)項
pcb->snd_lbb += len;
pcb->snd_buf -= len;
下一個序號增加相應(yīng)的數(shù)據(jù)長度。發(fā)送緩沖區(qū)減去已經(jīng)用掉的字節(jié)長度。
9、將發(fā)送隊列PBUF個數(shù)隊列送入pcb的snd_queuelen。
pcb->snd_queuelen = queuelen;
10、 是否需要設(shè)置PSH?果如是設(shè)置之。
到這里就完成了對數(shù)據(jù)存入隊列的全部過程了,當(dāng)然還有一種情況就是隊列不空的情況,那種情況在開始的時候要處理不過最終還是要計算出來到底有多少個pbuf在里面,是否超過設(shè)置最大值等依法炮制。
三:總結(jié)
對TCP數(shù)據(jù)的隊列分清3個地方,
1、是發(fā)送緩沖區(qū)
2、tcp段隊列
3、pbuf隊列
第一個是以字節(jié)為單位的,他被永久的記錄在pcb中的 pcb->snd_buf 中。
第二個是有多少個tcp段,他的單位是segment。他也被永久記錄在 pcb->unsent ,
第三個個是有多少個pbuf單位是pbuf, 他也永久的記錄在pcb->snd_queuelen 。
可見他們的關(guān)系基本上是找到PCB就能找到tcp段隊列,找到pbuf隊列,就能找到數(shù)據(jù)啦。而他們是按照先后順序被連接起來的。應(yīng)為幾乎所有的結(jié)構(gòu)形式都是以鏈表的形式存在。這樣做個隊列分分鐘的事了就。
等真的發(fā)送數(shù)據(jù)的時候就會從tcp段隊列取出來送到IP層逐層包裝發(fā)送。
每次看源碼我都懷著對原作者的極大崇敬,寫程序的小朋友確實不容易! 理解!理解!
|
|