找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 5318|回復(fù): 0
收起左側(cè)

TCP發(fā)送隊(duì)列原理

[復(fù)制鏈接]
ID:90014 發(fā)表于 2015-9-13 20:28 | 顯示全部樓層 |閱讀模式
TCP發(fā)送數(shù)據(jù)的時(shí)候并不是直接送出數(shù)據(jù)的,而是考慮了后續(xù)的數(shù)據(jù)一起發(fā)送所以,采用的是緩存隊(duì)列機(jī)制。

一、API參數(shù)
1、
pcb指針
控制塊記錄這個(gè)連接的幾乎所有的例如端口、IP地址、發(fā)送隊(duì)列、接收窗口、序號(hào)等等自然不用細(xì)說。作為一個(gè)全局結(jié)構(gòu)記錄著你開的TCP所有的信息。所以只要找到他就可以知道和當(dāng)前連接的所有有關(guān)結(jié)構(gòu)的值。因此在程序調(diào)試過程中可以利用他來鎖定相關(guān)的參數(shù)值等。所以這個(gè)指針就是指向了當(dāng)前建立的連接。
2、 arg指針
顯然這個(gè)是個(gè)萬能的運(yùn)載指針,這個(gè)是用來指向要發(fā)送的數(shù)據(jù)的,數(shù)據(jù)有兩種一種是駐留在內(nèi)存中也就是RAM中,另一種是駐留在FLASH中,也就是ROM中,在對(duì)待這兩者最大區(qū)別是pbuf的分配,如果駐留在內(nèi)存中,那么將會(huì)在RAM中開辟一塊數(shù)據(jù)區(qū)并把a(bǔ)rg指針指向的數(shù)據(jù)搬到這塊內(nèi)存中,前者不是協(xié)議棧管理而是用戶的內(nèi)存區(qū),后者是協(xié)議棧管理的,用戶無權(quán)操作。如果是ROM那么好了PBUF只有個(gè)頭而已,并沒有分配實(shí)際的數(shù)據(jù)區(qū),他的數(shù)據(jù)區(qū)直接引用FLASH的地址,也就是一般const修飾的,編譯器會(huì)把讓他放入FLASH中去。
3、len
數(shù)據(jù)長(zhǎng)度。要發(fā)送的數(shù)據(jù)有多么的長(zhǎng)!請(qǐng)告訴他。
4、
apiflags
復(fù)制(RAM/ROM)和PSH標(biāo)志
二、實(shí)現(xiàn)
1、如上參數(shù)傳進(jìn)來之后系統(tǒng)先要檢查是否具備發(fā)送的條件:

1)、當(dāng)前TCP狀態(tài)是否允許做發(fā)送數(shù)據(jù)的動(dòng)作?
2)、發(fā)送緩沖區(qū)的長(zhǎng)度是都還夠用?
3)、發(fā)送隊(duì)列是否超出最大的定義長(zhǎng)度?

如果全部通過則繼續(xù),否則自然不必說,返回錯(cuò)誤代碼給應(yīng)用程序。
2、  
queuelen = pcb->snd_queuelen
取出當(dāng)前發(fā)送隊(duì)列記錄里面的隊(duì)列長(zhǎng)度,、
3、找出來未發(fā)送隊(duì)列中是否空?這里只討論空的情況,也就是類似在第一次發(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中分配一個(gè)頭部,然后連接起來
也就是頭部+數(shù)據(jù)(指向ROM中)的結(jié)構(gòu)。

5、queuelen += pbuf_clen(p)
計(jì)算出來pbuf數(shù)量。由于鏈表的結(jié)構(gòu)所以實(shí)現(xiàn)超級(jí)簡(jiǎn)單
  while (p != NULL) {
    ++len;
    p = p->next;
  }
這樣 queuelen 就是pbuf的數(shù)量了也就是p->next的數(shù)量,通常是RAMp->next=NULL就是 ++len=1了。

6、建立segment
動(dòng)態(tài)從RAM中分配一塊MEMP_TCP_SEG 長(zhǎng)度的segment,
然后就是對(duì)這個(gè)TCP段進(jìn)行填充了,比如遠(yuǎn)端的端口本地的端口序號(hào)、pbuf位置等。重要的是TCP的序號(hào)。也會(huì)被填入此種段域中記錄。端口這些全部來自pcb中。
7、將段域?qū)懭雙cb的usent隊(duì)列中去。
queue = seg;
pcb->unsent = queue;
8、更新PCB相關(guān)項(xiàng)
  pcb->snd_lbb += len;
  pcb->snd_buf -= len;
下一個(gè)序號(hào)增加相應(yīng)的數(shù)據(jù)長(zhǎng)度。發(fā)送緩沖區(qū)減去已經(jīng)用掉的字節(jié)長(zhǎng)度。
9、將發(fā)送隊(duì)列PBUF個(gè)數(shù)隊(duì)列送入pcb的snd_queuelen。
pcb->snd_queuelen = queuelen;

10、 是否需要設(shè)置PSH?果如是設(shè)置之。

到這里就完成了對(duì)數(shù)據(jù)存入隊(duì)列的全部過程了,當(dāng)然還有一種情況就是隊(duì)列不空的情況,那種情況在開始的時(shí)候要處理不過最終還是要計(jì)算出來到底有多少個(gè)pbuf在里面,是否超過設(shè)置最大值等依法炮制。
三:總結(jié)

對(duì)TCP數(shù)據(jù)的隊(duì)列分清3個(gè)地方,

1、是發(fā)送緩沖區(qū)
2、tcp段隊(duì)列
3、pbuf隊(duì)列
第一個(gè)是以字節(jié)為單位的,他被永久的記錄在pcb中的
pcb->snd_buf 中。
第二個(gè)是有多少個(gè)tcp段,他的單位是segment。他也被永久記錄在 pcb->unsent ,
第三個(gè)個(gè)是有多少個(gè)pbuf單位是pbuf, 他也永久的記錄在pcb->snd_queuelen 。

可見他們的關(guān)系基本上是找到PCB就能找到tcp段隊(duì)列,找到pbuf隊(duì)列,就能找到數(shù)據(jù)啦。而他們是按照先后順序被連接起來的。應(yīng)為幾乎所有的結(jié)構(gòu)形式都是以鏈表的形式存在。這樣做個(gè)隊(duì)列分分鐘的事了就。
等真的發(fā)送數(shù)據(jù)的時(shí)候就會(huì)從tcp段隊(duì)列取出來送到IP層逐層包裝發(fā)送。

每次看源碼我都懷著對(duì)原作者的極大崇敬,寫程序的小朋友確實(shí)不容易。 理解!理解!










回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表