|
最近忙于手頭工作,而沒有太多時間搞那懸而未決的OOS隊列斷言失敗的問題。今晚猜測一下。
在測試中發現去掉這個隊列也是可以工作的,而且局域網內也是不會丟包,所以這個OOS隊列的作用就有待審視。
要想搞清楚這個OOS對列斷言失敗的情況下這個隊列的作用首先要從他的誕生之地說起。
IP層把數據交給tcp_input(struct pbuf *p, struct netif *inp),這就是開始了。
接著‘
iphdr = p->payload;
tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);
取出指向PBUF的載荷指針,就找到了那個所有的IP層運輸的TCP數據包。于是乎又取走了TCP的首部,按照TCPIP協議加載到對應的結構中,這樣一個TCP頭的句柄就產生了。
接下來是縮減PBUF有效指針,移動指針到拋去TCP首部的后邊TCP載荷數據上,這就是所謂的
pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))
下一步是拋卻多播和廣播如果有,因為在TCP中這些完全是操蛋的玩意。邊去!
下一步校驗這個TCP段是否正確。正確就繼續。
/* 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);
以上是取出來TCP的標志,就是所謂的RST FIN....等等所在的字段,然后保存在一個全局變量flags中,這很重要。
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next)
開始遍歷所有的活躍連接表。
如果活躍鏈表沒有,好開始遍歷另一個
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next)
就是所謂的等待隊列。
如果等待隊列也沒有再去遍歷另一個
for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next)
所謂的監聽列表,這就沒了。
如果當前列表中有,那好(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;
開始構建TCP段準備交付上層的結構準備工作。可見這個結構繼承PBUF
緊接著執行tcp_process(struct tcp_pcb *pcb)
開始處理TCP事務,其實是主要處理TCP的那狀態圖,根據
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
};
這個狀態處理各個分支的枝枝叉叉。最后鎖定這個函數
tcp_receive(struct tcp_pcb *pcb)
這個函數開始處理我兩件事3,一件事是TCP的ACK一件事是含有數據包的TCP報文。把它交給上層。但是他是如何交上去的,還得猜
if (flags & TCP_ACK) 這個就是處理TCP的ACK包,我們忽略這不是重點,因為我想看看底層數據如何向上的。向下換個選項
/* If the incoming segment contains data, we must process it
further. */
if (tcplen > 0) {。。。}
按照解釋上說的是只做三件事:
/* 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.
*/
終于出來OOS對列了,這個稱之為:out-of-sequence 隊列。如上文所述
這個OOS是用來存儲失序的SEQ號所排的隊,并且按照序號鏈接起來一并交付應用層處理,這種失序在局域網中小數據根本沒有。只有
在廣域網中或或者在局域網高速數據吞吐的情況下才會發生,尤其是廣域網中這種情況完全可能,
TCP的失序問題是這樣產生的
HOST端發送的數據經過若干道路由,有可能有的去天津饒了一圈,有的去海南轉了圈,再回來肯定有時間差,時間差很可能會發生序號小的后到達,而序號大的先到達,這是完全有可能的,這是OOS隊列起作用了,他會緩存住失序的TCP段,并發出失序應答。然后重新連接PBUF把序號鏈接的BUF連接重新分配后一并交付應用層。
而現在回到我最初的問題,那就是斷言失敗,在哪里斷言失敗呢,恰巧是在OOS對列的更新WIN處,也就是說這個OOS還要關心一個TCP的滑動窗口,而應用層遲遲沒能從更新WIN,也就是沒有及時的取走應用層的數據,而導致了WIN數據的推遲更新,更在搞的是還有的正確的數據包擠壓在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);
事故就不可避免的發生了,WIN值減到了負的!那是絕對不允許的。所以這里斷言失敗了。輸入了一個非法的值。這個值是TCP的滑動窗口不能為負造成的。W其實根源在于WIN值不大,大的話就不會出現負值鳥!!!!也就不會斷言失敗。
這種情況發生在網卡驅動速度慢,然后HOST快速發送的時候,出現。同樣的程序倘若放到一臺網卡快的就沒有這個問題。
現在只能猜到這里。表面上是WIN的值為負,實際上還有一些深層的背靜因素所左右。也從側面看到同樣的情況軟件情況下好的硬件就很重要了。
現在的用法是去掉OOS,這樣一來他就不會緩存失序的隊列,也就不會重新組裝底層的失序數據。肯定會丟失一些數據的。如果快速廣域網通信。
也許這是協議棧的BUG也許是我沒用精細。總之去掉OOS在網卡不是那么優秀的情況下會帶來一些意外收獲哦!!!!總比斷言失敗好吧。這只是個開始~~~~~~
最近面對伙計們關于未來的質問我確實心下慚愧,確實沒有什么成就,哎!狗屁一樣!簡直是槽糕透了,不過好在胡猜亂猜胡師傅 表示只要他那啥。。一定那啥...即使不那啥..胡猜亂猜胡師傅也自己認為很那啥了!
胡猜亂猜胡師傅老王頭!!!
20150129
日照比特
|
|