zoukankan      html  css  js  c++  java
  • 浅谈通信网络(五)——TCP层

    需求:

    通信的本质是进行信息的传递,而我们希望达到的效果总结起来就两点:快而准。

    背景:

    基于TCP/IP协议,运行在IP层上的流量将变得不可靠,无法仅仅靠IP层技术保障数据包的准确传达。

    TCP协议

    由此诞生了TCP协议来保证数据流量尽快准确的到达。

    我们来看下TCP协议采用了什么策略来保障的:

    首先是引入了确认机制,也就是在发送到对方后,对方得告知我们确实收到了。

    采用这种机制有一个前提,如果你发送很长一段数据包,需要拆分为多个数据包进行多次发送,由此你需要对这些数据包进行标序号,由此告知对方哪个数据包在前,哪个数据包在后,再便于TCP/IP协议栈组合之后,再传达给上层应用程序。另外对方也才能告诉你到底是哪块数据包传送成功了。

    比如,A发送了SYN(序列号)为1~25的数据包给B,B会返回一个ACK(=最大序列号+1),这里ACK=26。由此B告诉了A自己确实收到了。

    疑问:那B如何告知A没有收到该数据包勒?

    采用这种机制,A只有在接收到了ACK之后,才会发送下一个数据包。如果等待一段时间后,还未收到,将再次重新发送该数据包。

    缺陷:虽然保证了无误的发送,但是明显这种机制无法达到快速的发送。

               1.每次发送一个数据包,如果有丢失,需要等一个周期,才能知晓丢包,耗时太久;

               2.ACK其实是额外的网络开销,反复的发送加剧了网络负担。

    发送优化:

    a:

    选择一次性发送多个数据包。

    问题:一次性到底能够发送多少数据包呢?会不会发送太多造成对方无法接收?

    最大发送速率(当前窗口大小)取拥塞窗口和通告window窗口的最小值。

    b:

    由此引入通告window窗口机制,对方反馈自己能够一次性接收多大的数据包大小。(接收端流量控制窗口)

    c:

    发送方如何告诉对方自己已经将数据包发送完毕了,并清空本地发送缓存,采用将TCP的PUSH字段置一。(涉及伯克利算法)

    d:

    针对window大小的计算,需要知道发送多少个包,当然发送方得提前知道每个数据包能够发送多大呢?能够发送的最大值是多少?

    针对不同的二层网络,会有不同的数据包大小。比如以太网是1500字节,所谓的MTU。由此除去IP和TCP头部,就是1460字节。

    所以TCP协议定义了一个标识位叫MSS,以太网就是1460。也就是以太网接口能够发送的最大数据包大小为1460字节。

    在TCP建联的时候,不仅仅会协商window的大小,同时也会协商MSS的大小。发送方和接收方分别告知对方自己的MSS大小,最后以最小者为准,来指导发送方发送数据。

    缺陷:只是根据双方的MTU来计算的MSS,并无法知晓路径中网络设备的MTU大小,如果中间设备的MTU设备相对小些,将可能造成丢包。除非该数据包DF位为0,并且较小MTU的接口是三层口子,做的是三层转发。

    既然我们谈到了中间网路设备,发送的时候,不仅仅需要考虑收方的情况,还要预测两者之间的网络情况。到底带宽是多少,信号是否稳定,我们并无法直接获知,但可以采用一定的算法来优化。

    e:慢启动算法

    意思是慢慢提高发送数据包的速度,即发送单位时间内发送数据包的个数逐渐增加,指数级增长。

    f:拥塞窗口(发送方流量控制)

     前提:

      1. 最大发送速率(当前窗口大小)取拥塞窗口和通告window窗口的最小值。

      2.一般分组丢失,表示有拥塞。一般分组丢失有两种方式:

           I.发送超时(意思是发送了,但是超过一段时间未收到ACK);

           II.收到重复ACK(详情见后面解释)

    处理过程:

     拥塞窗口初始值是1个报文段,每收到一个ACK就增加一个报文段大小。

     由于最大发送速率前期主要受限于拥塞窗口。因此很容易得到拥塞窗口是以2为底的指数级增长。

     但这个最大发送速率的增长,在慢启动算法里面,有一个阈值,叫慢启动门限ssthresh,默认值是65535字节。

     当发送拥塞的时候,ssthresh会取值为当前窗口的一半(前面已经说了,该值取拥塞窗口和window窗口的最小值),并考虑是否将拥塞窗口置1(具体请看以下快速恢复算法)

     当未有拥塞,速率大小(当前最大速率大小或者叫当前窗口)超过ssthresh时候,进入拥塞避免阶段,拥塞窗口将从指数级增长,变为RRT时间内递增加1的线性增长。由此速率也会进入线性增长的过程,直到等于通告窗口。

    g:快速恢复算法

      当收到重复ACK后,拥塞窗口并不置1,进入慢启动。而是直接进入拥塞避免。这就是快速恢复算法。

     但是如果是超时的拥塞,拥塞窗口将置1,重新进入慢启动。

    接收端优化:

    a:

    ACK附带数据包发送。

    接收数据方,也往往需要发送数据包,在发送数据包的时候,将ACK附带一起发送出去。

    b.

    经受延迟的ACK。

    接收方在接收到数据后,并不立即发送ACK给发送方。己方有数据发送除外。一般采用的是等待200ms再发送最后一个ACK。

     好处:

    可以减少ACK的数据包量;

    但是重复ACK将不会被延迟,而是会立即发送。

    c.

     重复ACK(快速重传算法)

    当接收方发现在当前收到数据包里序列号前的数据包,并没有收到。则会将上次发送的ACK反馈给对方。由此告知对方两个信息:

    1.上次成功收到序列号后的数据我没有正确收到;2.你需要重新给我发送后续的数据包给我(一般就发送该ACK后的一个数据包,也就是丢失的数据包,不重新发送其他后续的数据包)。

         按照伯里克实现,其实发送方在收到三个重复ACK才会重新发送数据。原因是担心只是由于乱序造成的。而不是真实的丢包。

    其他

    naglle算法,

    一个TCP连接上最多只能有一个未被确认的未完成的小数据包,在该数据包被确认前,不允许再次发送其他小分组。

    本算法理论上可以提高互联网效率,但是却减慢了本地的发送速率。特别是在电脑上的人机交互设备上是必须关闭的。

    疑问:

    同一个连接,如果接收方未及时将数据包上传给上层应用程序,比如由于性能瓶颈,上层应用程序还未来得及获取数据包,而发送方又发来了数据。应该如何处理呢? 

    解答:

    1.接收方正常反馈ACK的时候,带上当前window=最大window-当前已占用缓存大小(一般为0,但也可能出现错误包等,造成的非0情况);

    2.发送方收到后,将调整发送数据为更新后的window

    (如果为0,则不会发送,直到收到接收方发过来新的window窗口大小)

    三次握手,四次断链(略)

     RRT关于时间戳(一般用于担心SYN反转)等(略)

    RST主动快速断链或者拒绝建链

    URG(略)

    TCP的四个定时器

    重传定时器、坚持定时器、保活定时器、2MSL定时器

    华为、思科等公司优化算法(再续)

    引读:https://user.qzone.qq.com/656272330/blog/1438714718

  • 相关阅读:
    数据持久化编程学习总结
    Boost Replaceable by C++11 language features or libraries
    【敬业福bug】支付宝五福卡敬业福太难求 被炒至200元
    由文字生成path后制作写字的动画
    CSS经典布局之弹性布局
    HDU2082 找单词 【母函数】
    HDOJ 题目2475 Box(link cut tree去点找祖先)
    DELPHI中MDI子窗口的关闭 和打开
    sql语句中日期相减的操作
    Delphi编码规范
  • 原文地址:https://www.cnblogs.com/daiaiai/p/10089362.html
Copyright © 2011-2022 走看看