找回密码
 立即注册

QQ登录

只需一步,快速开始

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

TCP拥塞控制之我见

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


由于TCP和UDP使用同样的IP层所以对于IP层报文来说管你什协议都是他的运送数据了,事实上IP层检查了是否本地的IP然后送往上层。UDP几乎等同于原始数据,根据《TCPIP协议卷三》之描述说UDP要实现可靠连接就等于书写了TCP,TCP是面向连接的,而UDP不是,书中用socket构建了TCP的SYN机理。所以我决定先对TCP下手。而TCP的庞大那是在之前早有心理准备的,你越是畏惧越是不敢,越是不敢越是畏惧,所以我想好了,要挑战TCP源码。只有源码可以带来更多的知识和编程思想,以及编程的实现。而TCP的拥塞控制只是个开始.....就像大海的一滴水,长远了...也许一生都未必入门...可笑的是我明知道却依旧执拗于此。。。。

测试环境:
电脑一小台
路由器一小个
开发板一小只,
网线一小咕噜。

TCP拥塞控制问题的引入:
在TCP连接建立之后,HSOT以最高的数据流向外推送数据得到的结果是发了一会断开了,不仅要问问啥断开?为什么会停止?这是我在试验中遇到的。倘若没有大数据,就不会停止,这是为什么?

问题表现:
以上问题经过wrieshark抓包后可以看到所有的数据。首先是SYN以及SYN所对应的ACK,再往后就是所谓的PUSH和UPADTA WIN 和WIN=0的报告包。还有部分重传和丢失的包,最后是RST包。

按照这个来分析:首先3次握手后数据短时间内达到一个很高的值,此时大量的数据包存在于网络中,路由器转发中,memalloc()执行频度频繁。大量的数据包被拥入TCP中处理,可惜的是上层处理不过来哦,因为在RECV后面有个SEND,所以迟滞了。幸运的是TCP本身可以调整流量控制收发端的BUFF不至于溢出或者说是匹配处理能力,那么他是如何做到的呢?win 减到一定的数额就又回到原先的SYN值,在哪里更新的?那就得看代码了:
if (sys_arch_mbox_fetch(conn->recvmbox, (void *)&p, conn->recv_timeout)==SYS_ARCH_TIMEOUT) {
      memp_free(MEMP_NETBUF, buf);
      conn->err = ERR_TIMEOUT;
      return NULL;
  buf->p = p;
    buf->ptr = p;
    buf->port = 0;
    buf->addr = NULL;

    /* Let the stack know that we have taken the data. */
    msg.function = do_recv;
    msg.msg.conn = conn;
    if (buf != NULL) {
      msg.msg.msg.r.len = buf->p->tot_len;
    } else {
      msg.msg.msg.r.len = 1;
    }
    TCPIP_APIMSG(&msg);

上面的代码显示了我们从邮箱中获取数据后系统加载了一个
msg.function = do_recv;    msg.msg.conn = conn;
这个代码是什么呢》?继续下一步:
         {
          tcp_recved(msg->conn->pcb.tcp, msg->msg.r.len);
        }
哦这么个玩意,继续向下:
pcb->rcv_wnd += len;
  if (pcb->rcv_wnd > TCP_WND)
    pcb->rcv_wnd = TCP_WND;

  wnd_inflation = tcp_update_rcv_ann_wnd(pcb);

  /* If the change in the right edge of window is significant (default
   * watermark is TCP_WND/2), then send an explicit update now.
   * Otherwise wait for a packet to be sent in the normal course of
   * events (or more window to be available later) */
  if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD)
    tcp_ack_now(pcb);
到这里终于知道了,原来TCPWEINDOW在这里更新啊。不错不错。什么时候更新呢?那是有条件的

这就是条件。
于是解决了第一个疑惑那就WIN的UPDATA.
接下来就是WIN=0?为何?
其实上个问题已经解释清楚了,我们只需要引深一下就好,那就是WIN只有在应用程序读走TCP推上来的BUF后才会更新WIN,否则的话就会对WIN进行相应载荷的减操作。如果应用程序很忙,你叫他他并不响应,此时WIN就处于不断下滑不断减少的境地。然后WIN=0,最终用完了WIN,因为应用程序很忙嘛,他没有时间了作别的他得处理,至于为什么忙,比如一个更改优先级的线程就绪了执行中而读取他的线程挂起等待,这就是典型的。很多原因的。于是WIN=0了。HOST一看WIN=0,就想:我擦:竟然没有空间了?好吧我停止吧。你没地方让我的人过去呆在那里?于是乎他等了一会就挂断聊!一个RST随之产生OK,这样似乎很符合抓包获取的流程。事实应该89不离十。这里面还有TCP的另一个概念,就是延时的概念,也就是PUSH和ACK并存的时候,在TCP数据发送的时候TCP并不是立即发送而是有规律的推迟发送,以利用一个包传送更多的信息,这是另一个重要的模块。不过这里不讨论此方面的问题。
再回到最初的地方,如果按照以上所述则则应用层处理的越快UPDATA应该每次都很顺利的更新。试试看
          send(tcp_clint_sock, recv_data,bytes_received, 0);
去掉邮箱之后的此函数之后,再去开wireshak 抓取的包就是一个完美的情形,也就是预期的结果。tcp流一直持续,并没有断过了,这印证了之前的推论和实际情况之间是等价的。也就是说应用层只有尽快的处理完数据才可以流畅。这样一个TCP流控就展现在面前:

例如我的demo WIN=4096


A:TCP建立连接的时候告诉主机我的接收窗口是4096 MSS=1024
B:主机知道了我的接收窗口是4096,报文长度1K,这样主机就有数了,然后主机报告他自己的接收窗口
C:主机开始发送数据。每次发送都会+上窗口
D:从机开始回复数据。每次都会+上窗口
E:如果数据太快上层处理不过来则窗口必然会小于临界值,极端是=0,那主机有数了就会缩减。直到窗口用完为0,B表示实在是太快了,处理不过来了。只能终止。
F:应用层适当的根据阀值更新窗口,以接收更多的字节流


也就是说流量控制实际上就是所谓的滑动窗口在起作用滴。

今天和某君讨论HTTP的问题,由于某君是裸奔的所以它不存在邮箱的问题。这样就有点麻烦了,得使用底层丢上来的BUF,结果他又忘记了在APP程序中是free掉内存,最终WEB刷新几下就死翘翘聊。如果这里采用邮箱就不会出现内存泄露这样的情况,如下:
/* copy the contents of the received buffer into
    the supplied memory pointer mem */
    netbuf_copy_partial(buf, (u8_t*)mem + off, copylen, sock->lastoffset);
系统会自动的吧BUF释放掉并把数据COPY到应用层里面,这样就实现了绝对的分层!这才是绝对的分层。否则就是伪分层的。应用层还是脱离不开底层的。这也是覆盖OS和不覆盖OS的区别之一吧。
但是使用OS时有代价的,要牺牲更多的内存和时间性。比无OS系统响应时间更慢,这是一定的。也是看应用而选择的特殊考量。

如今互联网普遍的时代,有谁会想到这事:
1977年11月22日,载有一个无线传输器的一辆改装厢式货车通过卫星从旧金山向挪威发送了一个信号,然后又把这个信号传输回到加利福尼亚州,标志着TCP/IP协议第一次被用来在三个独立的电脑网络之间发送信号。
即使在今天我们打的DOTA我们玩的星际。QQ email 没有一个不是基于TCPIP协议。

致敬 Bob Kahn&Vint Cerf&&Adam

老伟
日照











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

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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