|
最近忙于手头工作,而没有太多时间搞那悬而未决的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
日照比特
|
|