|
最近忙于手頭工作,而沒(méi)有太多時(shí)間搞那懸而未決的OOS隊(duì)列斷言失敗的問(wèn)題。今晚猜測(cè)一下。
在測(cè)試中發(fā)現(xiàn)去掉這個(gè)隊(duì)列也是可以工作的,而且局域網(wǎng)內(nèi)也是不會(huì)丟包,所以這個(gè)OOS隊(duì)列的作用就有待審視。
要想搞清楚這個(gè)OOS對(duì)列斷言失敗的情況下這個(gè)隊(duì)列的作用首先要從他的誕生之地說(shuō)起。
IP層把數(shù)據(jù)交給tcp_input(struct pbuf *p, struct netif *inp),這就是開(kāi)始了。
接著‘
iphdr = p->payload;
tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);
取出指向PBUF的載荷指針,就找到了那個(gè)所有的IP層運(yùn)輸?shù)腡CP數(shù)據(jù)包。于是乎又取走了TCP的首部,按照TCPIP協(xié)議加載到對(duì)應(yīng)的結(jié)構(gòu)中,這樣一個(gè)TCP頭的句柄就產(chǎn)生了。
接下來(lái)是縮減PBUF有效指針,移動(dòng)指針到拋去TCP首部的后邊TCP載荷數(shù)據(jù)上,這就是所謂的
pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))
下一步是拋卻多播和廣播如果有,因?yàn)樵赥CP中這些完全是操蛋的玩意。邊去!
下一步校驗(yàn)這個(gè)TCP段是否正確。正確就繼續(xù)。
/* Convert fields in TCP header to host byte order. */
tcphdr->src = ntohs(tcphdr->src);
tcphdr->dest = ntohs(tcphdr->dest);
seqno = tcphdr->seqno = ntohl(tcphdr->seqno);
ackno = tcphdr->ackno = ntohl(tcphdr->ackno);
tcphdr->wnd = ntohs(tcphdr->wnd);
顯然......... 那么。。。。
flags = TCPH_FLAGS(tcphdr); tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);
以上是取出來(lái)TCP的標(biāo)志,就是所謂的RST FIN....等等所在的字段,然后保存在一個(gè)全局變量flags中,這很重要。
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next)
開(kāi)始遍歷所有的活躍連接表。
如果活躍鏈表沒(méi)有,好開(kāi)始遍歷另一個(gè)
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next)
就是所謂的等待隊(duì)列。
如果等待隊(duì)列也沒(méi)有再去遍歷另一個(gè)
for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next)
所謂的監(jiān)聽(tīng)列表,這就沒(méi)了。
如果當(dāng)前列表中有,那好(pcb != NULL)必定成立,則
inseg.next = NULL;
inseg.len = p->tot_len;
inseg.dataptr = p->payload;
inseg.p = p;
inseg.tcphdr = tcphdr;
recv_data = NULL;
recv_flags = 0;
開(kāi)始構(gòu)建TCP段準(zhǔn)備交付上層的結(jié)構(gòu)準(zhǔn)備工作。可見(jiàn)這個(gè)結(jié)構(gòu)繼承PBUF
緊接著執(zhí)行tcp_process(struct tcp_pcb *pcb)
開(kāi)始處理TCP事務(wù),其實(shí)是主要處理TCP的那狀態(tài)圖,根據(jù)
enum tcp_state {
CLOSED = 0,
LISTEN = 1,
SYN_SENT = 2,
SYN_RCVD = 3,
ESTABLISHED = 4,
FIN_WAIT_1 = 5,
FIN_WAIT_2 = 6,
CLOSE_WAIT = 7,
CLOSING = 8,
LAST_ACK = 9,
TIME_WAIT = 10
};
這個(gè)狀態(tài)處理各個(gè)分支的枝枝叉叉。最后鎖定這個(gè)函數(shù)
tcp_receive(struct tcp_pcb *pcb)
這個(gè)函數(shù)開(kāi)始處理我兩件事3,一件事是TCP的ACK一件事是含有數(shù)據(jù)包的TCP報(bào)文。把它交給上層。但是他是如何交上去的,還得猜
if (flags & TCP_ACK) 這個(gè)就是處理TCP的ACK包,我們忽略這不是重點(diǎn),因?yàn)槲蚁肟纯吹讓訑?shù)據(jù)如何向上的。向下?lián)Q個(gè)選項(xiàng)
/* If the incoming segment contains data, we must process it
further. */
if (tcplen > 0) {。。。}
按照解釋上說(shuō)的是只做三件事:
/* This code basically does three things:
+) If the incoming segment contains data that is the next
in-sequence data, this data is passed to the application. This
might involve trimming the first edge of the data. The rcv_nxt
variable and the advertised window are adjusted.
+) If the incoming segment has data that is above the next
sequence number expected (->rcv_nxt), the segment is placed on
the ->ooseq queue. This is done by finding the appropriate
place in the ->ooseq queue (which is ordered by sequence
number) and trim the segment in both ends if needed. An
immediate ACK is sent to indicate that we received an
out-of-sequence segment.
+) Finally, we check if the first segment on the ->ooseq queue
now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
rcv_nxt > ooseq->seqno, we must trim the first edge of the
segment on ->ooseq before we adjust rcv_nxt. The data in the
segments that are now on sequence are chained onto the
incoming segment so that we only need to call the application
once.
*/
終于出來(lái)OOS對(duì)列了,這個(gè)稱(chēng)之為:out-of-sequence 隊(duì)列。如上文所述
這個(gè)OOS是用來(lái)存儲(chǔ)失序的SEQ號(hào)所排的隊(duì),并且按照序號(hào)鏈接起來(lái)一并交付應(yīng)用層處理,這種失序在局域網(wǎng)中小數(shù)據(jù)根本沒(méi)有。只有
在廣域網(wǎng)中或或者在局域網(wǎng)高速數(shù)據(jù)吞吐的情況下才會(huì)發(fā)生,尤其是廣域網(wǎng)中這種情況完全可能,
TCP的失序問(wèn)題是這樣產(chǎn)生的
HOST端發(fā)送的數(shù)據(jù)經(jīng)過(guò)若干道路由,有可能有的去天津饒了一圈,有的去海南轉(zhuǎn)了圈,再回來(lái)肯定有時(shí)間差,時(shí)間差很可能會(huì)發(fā)生序號(hào)小的后到達(dá),而序號(hào)大的先到達(dá),這是完全有可能的,這是OOS隊(duì)列起作用了,他會(huì)緩存住失序的TCP段,并發(fā)出失序應(yīng)答。然后重新連接PBUF把序號(hào)鏈接的BUF連接重新分配后一并交付應(yīng)用層。
而現(xiàn)在回到我最初的問(wèn)題,那就是斷言失敗,在哪里斷言失敗呢,恰巧是在OOS對(duì)列的更新WIN處,也就是說(shuō)這個(gè)OOS還要關(guān)心一個(gè)TCP的滑動(dòng)窗口,而應(yīng)用層遲遲沒(méi)能從更新WIN,也就是沒(méi)有及時(shí)的取走應(yīng)用層的數(shù)據(jù),而導(dǎo)致了WIN數(shù)據(jù)的推遲更新,更在搞的是還有的正確的數(shù)據(jù)包擠壓在OOS中,最要命的是WIN值并不多了,然后
pcb->rcv_nxt += TCP_TCPLEN(cseg);
ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
pcb->rcv_wnd >= TCP_TCPLEN(cseg));
pcb->rcv_wnd -= TCP_TCPLEN(cseg);
tcp_update_rcv_ann_wnd(pcb);
事故就不可避免的發(fā)生了,WIN值減到了負(fù)的!那是絕對(duì)不允許的。所以這里斷言失敗了。輸入了一個(gè)非法的值。這個(gè)值是TCP的滑動(dòng)窗口不能為負(fù)造成的。W其實(shí)根源在于WIN值不大,大的話(huà)就不會(huì)出現(xiàn)負(fù)值鳥(niǎo)!!!!也就不會(huì)斷言失敗。
這種情況發(fā)生在網(wǎng)卡驅(qū)動(dòng)速度慢,然后HOST快速發(fā)送的時(shí)候,出現(xiàn)。同樣的程序倘若放到一臺(tái)網(wǎng)卡快的就沒(méi)有這個(gè)問(wèn)題。
現(xiàn)在只能猜到這里。表面上是WIN的值為負(fù),實(shí)際上還有一些深層的背靜因素所左右。也從側(cè)面看到同樣的情況軟件情況下好的硬件就很重要了。
現(xiàn)在的用法是去掉OOS,這樣一來(lái)他就不會(huì)緩存失序的隊(duì)列,也就不會(huì)重新組裝底層的失序數(shù)據(jù)。肯定會(huì)丟失一些數(shù)據(jù)的。如果快速?gòu)V域網(wǎng)通信。
也許這是協(xié)議棧的BUG也許是我沒(méi)用精細(xì)。總之去掉OOS在網(wǎng)卡不是那么優(yōu)秀的情況下會(huì)帶來(lái)一些意外收獲哦!!!!總比斷言失敗好吧。這只是個(gè)開(kāi)始~~~~~~
最近面對(duì)伙計(jì)們關(guān)于未來(lái)的質(zhì)問(wèn)我確實(shí)心下慚愧,確實(shí)沒(méi)有什么成就,哎!狗屁一樣!簡(jiǎn)直是槽糕透了,不過(guò)好在胡猜亂猜胡師傅 表示只要他那啥。。一定那啥...即使不那啥..胡猜亂猜胡師傅也自己認(rèn)為很那啥了!
胡猜亂猜胡師傅老王頭!!!
20150129
日照比特
|
|