找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4082|回复: 1
打印 上一主题 下一主题
收起左侧

TCP的OOS 队列之胡猜乱猜胡师傅

[复制链接]
跳转到指定楼层
楼主
ID:82781 发表于 2015-6-13 00:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式


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





分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩

相关帖子

回复

使用道具 举报

沙发
ID:82781 发表于 2015-6-13 00:41 | 只看该作者
        Send


1、对载荷进行填充,填充目标地址。校验等
2、SndState = STATE_M_TX_XMIT;激活发送,打开发送接口驱动PortEnable( FALSE, TRUE );
3、发送 /* check if we are finished. */
        if( SndBufferCount != 0 )
        {
            PortPutByte( ( CHAR )*pucMasterSndBufferCur );
            pucMasterSndBufferCur++;  /* next byte in sendbuffer. */
            SndBufferCount--;
        }
4、发送完毕,开始判断当前是否广播然后区别对待(打开接受关闭发送,打开接收就是要接受回应的。)

/* If the frame is broadcast ,master will enable timer of convert delay,
             * else master will enable timer of respond timeout. */
            if ( xFrameIsBroadcast == TRUE )
            {
            vMBMasterPortTimersConvertDelayEnable( );
            }
            else
            {
            vMBMasterPortTimersRespondTimeoutEnable( );
            }

两种情况都是开启定时器计时开始,但是时间长短不一样而已。
5、关闭POLL。
6、开始等待延时结束
7、延时完成回调函数激活
/* A frame was send finish and convert delay or respond timeout expired.
* If the frame is broadcast,The master will idle,and if the frame is not
* broadcast.Notify the listener process error.*/
向应用程序报告当前的情况,是广播的话就是当前延时结束,是发送的话就是没有回应应该报错误。这样一个超时错误响应的就产生了
于是通过一个队列接口通知应用即可】】
PortEventPost(EV_MASTER_ERROR_PROCESS);
8、  关闭定时器,发送状态重新回到        eSndState = STATE_M_TX_IDLE;


值得注意的是这是非正常情况,正常是
如果收到从机的回应主机会立即关闭发送等待定时器,并重新标定发送状态为空闲
case STATE_M_RX_IDLE:
     PortTimersDisable( );
            eSndState = STATE_M_TX_IDLE;
这个是在接受文件中定义的!
就是这些
2015年3月15日
比特
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表