TCP协议(下)

TCP滑动窗口

发送端

  • LastByteAcked:第一部分和第二部分的分界线
  • LastByteSent:第二部分和第三部分的分界线
  • LastByteAcked + AdvertisedWindow:第三部分和第四部分的分界线

第一部分:发送了并且已经确认的。
第二部分:发送了并且尚未确认的。
第三部分:没有发送,但是已经等待发送的。
第四部分:没有发送,并且暂时还不会发送的。

接收端

  • MaxRcvBuffer:最大缓存的量;
  • LastByteRead 之后是已经接收了,但是还没被应用层读取的;
  • NextByteExpected 是第一部分和第二部分的分界线。

第一部分:接受并且确认过的。
第二部分:还没接收,但是马上就能接收的。
第三部分:还没接收,也没法接收的。

顺序问题与丢包问题

还是刚才的图,在发送端来看,1、2、3 已经发送并确认;4、5、6、7、8、9 都是发送了还没确认;
10、11、12 是还没发出的;13、14、15 是接收方没有空间,不准备发的。

在接收端来看,1、2、3、4、5 是已经完成 ACK,但是没读取的;6、7 是等待接收的;8、9 是已经接
收,但是没有 ACK 的

发送端和接收端当前的状态如下:

  • 1、2、3 没有问题,双方达成了一致。
  • 4、5 接收方说 ACK 了,但是发送方还没收到,有可能丢了,有可能在路上。
  • 6、7、8、9 肯定都发了,但是 8、9 已经到了,但是 6、7 没到,出现了乱序,缓存着但是没办法ACK。

确认与重发的机制

假设 4 的确认到了,不幸的是,5 的 ACK 丢了,6、7 的数据包丢了,这该怎么办呢?
      一种方法就是超时重试,也即对每一个发送了,但是没有 ACK 的包,都有设一个定时器,超过了一定的时间,就重新尝试。但是这个超时的时间如何评估呢?这个时间不宜过短,时间必须大于往返时间RTT,否则会引起不必要的重传。也不宜过长,这样超时时间变长,访问就变慢了。估计往返时间,需要 TCP 通过采样 RTT 的时间,然后进行加权平均,算出一个值,而且这个值还是要不断变化的,因为网络状况不断的变化。除了采样 RTT,还要采样 RTT 的波动范围,计算出一个估计的超时时间。由于重传时间是不断变化的,我们称为自适应重传算法(Adaptive RetransmissionAlgorithm)。
      如果过一段时间,5、6、7 都超时了,就会重新发送。接收方发现 5 原来接收过,于是丢弃 5;6 收到了,发送 ACK,要求下一个是 7,7 不幸又丢了。当 7 再次超时的时候,有需要重传的时候,TCP 的策略是超时间隔加倍。每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。

超时触发重传存在的问题是,超时周期可能相对较长。那是不是可以有更快的方式呢?
      有一个可以快速重传的机制,当接收方收到一个序号大于下一个所期望的报文段时,就检测到了数据流中的一个间格,于是发送三个冗余的 ACK,客户端收到后,就在定时器过期之前,重传丢失的报文段。例如,接收方发现 6、8、9 都已经接收了,就是 7 没来,那肯定是丢了,于是发送三个 6 的 ACK,要求下一个是 7。客户端收到 3 个,就会发现 7 的确又丢了,不等超时,马上重发。
      还有一种方式称为Selective Acknowledgment (SACK)。这种方式需要在 TCP 头里加一个 SACK 的东西,可以将缓存的地图发送给发送方。例如可以发送 ACK6、SACK8、SACK9,有了地图,发送方一下子就能看出来是 7 丢了。

流量控制问题

再来看流量控制机制,在对于包的确认中,同时会携带一个窗口的大小。
   先假设窗口不变的情况,窗口始终为 9。4 的确认来的时候,会右移一个,这个时候第 13 个包也可以发送了。

这个时候,假设发送端发送过猛,会将第三部分的 10、11、12、13 全部发送完毕,之后就停止发送
了,未发送可发送部分为 0。

当对于包 5 的确认到达的时候,在客户端相当于窗口再滑动了一格,这个时候,才可以有更多的包可以发送了,例如第 14 个包才可以发送。

如果接收方实在处理的太慢,导致缓存中没有空间了,可以通过确认信息修改窗口的大小,甚至可以设置为 0,则发送方将暂时停止发送
假设一个极端情况,接收端的应用一直不读取缓存中的数据,当数据包 6 确认后,窗口大小就不能再是 9 了,就要缩小一个变为 8。

这个新的窗口 8 通过 6 的确认消息到达发送端的时候,你会发现窗口没有平行右移,而是仅仅左面的边右移了,窗口的大小从 9 改成了 8。

如果接收端还是一直不处理数据,则随着确认的包越来越多,窗口越来越小,直到为 0。

当这个窗口通过包 14 的确认到达发送端的时候,发送端的窗口也调整为 0,停止发送

如果这样的话,发送方会定时发送窗口探测数据包,看是否有机会调整窗口的大小。当接收方比较慢的时候,要防止低能窗口综合征,别空出一个字节来就赶快告诉发送方,然后马上又填满了,可以当窗口太小的时候,不更新窗口,直到达到一定大小,或者缓冲区一半为空,才更新窗口。这就是我们常说的流量控制。

拥塞控制

拥塞控制的问题,也是通过窗口的大小来控制的,前面的滑动窗口 rwnd 是怕发送方
     把接收方缓存塞满,而拥塞窗口 cwnd,是怕把网络塞满。这里有一个公式 LastByteSent - LastByteAcked <= min {cwnd, rwnd} ,是拥塞窗口和滑动窗口共同控制发送的速度。

水管有粗细,网络有带宽,也即每秒钟能够发送多少数据;水管有长度,端到端有时延。在理想状态下,水管里面水的量 = 水管粗细 x 水管长度。对于到网络上,通道的容量 = 带宽 × 往返延迟。
     如果我们设置发送窗口,使得发送但未确认的包为为通道的容量,就能够撑满整个管道。

如图所示,假设往返时间为 8s,去 4s,回 4s,每秒发送一个包,每个包 1024byte。已经过去了 8s,则 8 个包都发出去了,其中前 4 个包已经到达接收端,但是 ACK 还没有返回,不能算发送成功。5-8后四个包还在路上,还没被接收。这个时候,整个管道正好撑满,在发送端,已发送未确认的为 8 个包,正好等于带宽,也即每秒发送 1 个包,乘以来回时间 8s。
如果我们在这个基础上再调大窗口,使得单位时间内更多的包可以发送,会出现什么现象呢?
      我们来想,原来发送一个包,从一端到达另一端,假设一共经过四个设备,每个设备处理一个包时间耗费 1s,所以到达另一端需要耗费 4s,如果发送的更加快速,则单位时间内,会有更多的包到达这些中间设备,这些设备还是只能每秒处理一个包的话,多出来的包就会被丢弃,这是我们不想看到的。这个时候,我们可以想其他的办法,例如这个四个设备本来每秒处理一个包,但是我们在这些设备上加缓存,处理不过来的在队列里面排着,这样包就不会丢失,但是缺点是会增加时延,这个缓存的包,4s肯定到达不了接收端了,如果时延达到一定程度,就会超时重传,也是我们不想看到的。
       于是 TCP 的拥塞控制主要来避免两种现象,包丢失和超时重传。一旦出现了这些现象就说明,发送速度太快了,要慢一点。但是一开始我怎么知道速度多快呢,我怎么知道应该把窗口调整到多大呢?
       如果我们通过漏斗往瓶子里灌水,我们就知道,不能一桶水一下子倒进去,肯定会溅出来,要一开始慢慢的倒,然后发现总能够倒进去,就可以越倒越快。这叫作慢启动

一条 TCP 连接开始,cwnd 设置为一个报文段,一次只能发送一个;当收到这一个确认的时候,cwnd加一,于是一次能够发送两个;当这两个的确认到来的时候,每个确认 cwnd 加一,两个确认 cwnd 加二,于是一次能够发送四个;当这四个的确认到来的时候,每个确认 cwnd 加一,四个确认 cwnd 加四,于是一次能够发送八个。可以看出这是指数性的增长
      涨到什么时候是个头呢?有一个值 ssthresh 为 65535 个字节,当超过这个值的时候,就要小心一点了,不能倒这么快了,可能快满了,再慢下来。
每收到一个确认后,cwnd 增加 1/cwnd,我们接着上面的过程来,一次发送八个,当八个确认到来的时候,每个确认增加 1/8,八个确认一共 cwnd 增加 1,于是一次能够发送九个,变成了线性增长。
      但是线性增长还是增长,还是越来越多,直到有一天,水满则溢,出现了拥塞,这时候一般就会一下子降低倒水的速度,等待溢出的水慢慢渗下去。
拥塞的一种表现形式是丢包,需要超时重传,这个时候,将 sshresh 设为 cwnd/2,将 cwnd 设为 1,重新开始慢启动。这真是一旦超时重传,马上回到解放前。但是这种方式太激进了,将一个高速的传输速度一下子停了下来,会造成网络卡顿。
      当接收端发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速的重传,不必等待超时再重传。TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,cwnd 减半为 cwnd/2,然后 sshthresh = cwnd,当三个包返回的时候,cwnd = sshthresh +3,也就是没有一夜回到解放前,而是还在比较高的值,呈线性增长

TCP BBR 拥塞算法

它企图找到一个平衡点,就是通过不断的加快发送速度,将管道填满,但是不要填满中间设备的缓存,因为这样时延会增加,在这个平衡点可以很好的达到高带宽和低时延的平衡

原文地址:https://www.cnblogs.com/menkeyi/p/11306163.html

时间: 2025-01-17 23:00:43

TCP协议(下)的相关文章

TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q

TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q 一.TCP协议下的服务端并发 ''' 将不同的功能尽量拆分成不同的函数,拆分出来的功能可以被多个地方使用 TCP服务端实现并发 1.将连接循环和通信循环拆分成不同的函数 2.将通信循环做成多线程 ''' # 服务端 import socket from threading import Thread ''' 服务端 要有固定的IP和PORT 24小时不间断提供服务 能够支持并发 ''' server = sock

TCP协议下的粘包问题

TCP协议下的粘包问题 粘包问题出现在TCP协议下,在UDP协议下不会出现粘包的问题. 粘包问题出现的原因: 应用层被成为应用元,操作系统被被称为系统元 合包机制:在TCP协议下有一个合包机制,当应用层传输过来数据后,如果数据较小,并且连续多次传输,此时nagle算法会对把多个数据进行 打包,统一发送给接收方, 好处是:减少了网络资源的消耗,接收方只需要给发送方回传一份回执即可,如果不进行合包操作,每发送一条数据,接收方就需要回一份回执,会大量消耗网络资源. 弊端是:由于TCP协议下字节流没有明

基于tcp协议下粘包现象和解决方案

一.缓冲区 每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区.write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器.一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情.TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决

基于TCP协议下的socket编程

socket: TCP/IP协议中一个端口号和一个IP地址绑定在一起就生成一个socket就表示了网络中唯一的一个进程,它是全双工的工作方式. 基于TCP的socket编程 函数的使用: 1.socket()         #include <sys/types.h>          /* See NOTES */        #include <sys/socket.h>        int socket(int domain, int type, int protoco

TCP协议下Socket接收比较慢点原因

在做一个游戏,发现阻塞和异步方式接受服务端的包都很反应很慢(不是网速问题),本机访问本机没这个问题,局域网有感觉200ms左右的延迟,部分机型感觉明显,部分不明显.找了很多资料,查到下面的文章,总算明白了. https://support.microsoft.com/zh-cn/kb/214397 设计问题-通过使用 Winsock TCP 发送较小的数据段  电子邮件  打印 重要说明:本文是由 Microsoft 机器翻译软件进行的翻译并可能由 Microsoft 社区通过社区翻译机构(

tcp协议下粘包问题的产生及解决方案

1.粘包产生原因: (1)TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段.若连续几次需要send的数据都很少,通常TCP会根据优化算法(Nagle)把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据: (2)接收方不知道消息之间的界限,不知道一次性提取多少字节的数据:接收时有字节的限制,如果超过这个限制没有接收完的会留在 操作系统缓存,下次再执行命令获取结果时,优先收取上次命令结果残留的信息: 注:UDP是无连接的,面向消息的,提供高效率服务.不会使用

打通windows和Linux下的传输问题解决只能使用SSH协议下的22端口来传输文件(Openssh for windows)

目的:打通windows和Linux下的传输问题解决只能使用SSH协议下的22端口来传输文件 环境: windows IP192.168.1.120 Linux IP192.168.101 方法: 安装opensshfor windows (次软件开源并增加ssh协议将windows模拟成Unix环境) 安装完软件导入本地用户或者域用户 进入到bin目录下面执行 cd "c:\Program Files(x86)\OpenSSH\bin" mkgroup -l >>..\e

Java通信编程中调用UDP协议与TCP协议之间的不同

UDP协议与TCP协议之间的区别不再分析,主要是分析一下这两个协议在Java通信编程中是如何被使用的. 首先介绍TCP,对于TCP,Java语言为它提供了良好的支持.建立TCP通信,首先需要构建服务器,并且得 到服务器的IP和端口号. TCP协议下的Socket类: java.net.Socket类代表客户端连接 java.net.ServerSocket类代表服务器端连接 Server:ServerSocket ss = new ServerSocket(5000); //创建服务器端的soc

Java中的基于Tcp协议的网络编程

一:网络通信的三要素? IP地址     端口号     通信协议 IP地址:是网络中设备的通信地址.由于IP地址不易记忆,故可以使用主机名.本地环回地址,127.0.0.1   本地主机名localhost 端口号:发送端准备的数据要发送到指定的目的应用程序上,为了标识这些应用程序,所以用网络数字来标识这些不同的应用程序,这些数 字称为端口号.端口号是不同进程之间的标识.一般来说,有0~65535的端口可供使用,但是1~1024系统使用,或者称作保留端口. 通信协议:指定义的通信规则,这个规则

TCP协议详解(下)

 TCP协议详解 TCP状态转移 TCP连接的任意一端在任一时刻都处于某种状态,当前状态可以通过netstat命令查看,这里我们主要讨论TCP连接葱白建立到关闭的整个过程中通信两端状态的变化.如图是TCP状态转移过程. 图中,粗虚线表示典型的服务器连接的状态转移:粗实线显示典型的客户端连接的状态转移. TCP状态转移总图 服务器转移过程,这里我们说的连接状态指定是该连接的服务器状态. 服务器通过listen系统调用进入LISTEN状态,被动等待客户端连接,因此执行的是所谓的被动打开.服务器一