《计算机网络自顶向下方法》第三章 传输层

版权声明:本文为博主原创文章,愿转就转吧~ https://blog.csdn.net/slx3320612540/article/details/82832583

计算机网络——传输层


传输层位于应用层和网络层之间,是分层的网络体系结构中重要的部分,该层为运行在不同主机上的应用进程提供直接的通信服务起着至关重要的作用。在这里我们将讨论两个大的问题:将网络层在不同端系统之间的通信服务扩充到运行在两个端系统上不同的应用层进程之间的通信服务(如何实现进城之间通信的可靠传输)和控制传输层实体的传输速度以避免网络拥塞或者从网络拥塞中恢复过来,这里需要考虑的有拥塞的后果和原因以及常见的拥塞控制手段,特别的,我们将了解TCP协议中的拥塞控制。

传输层概述

传输层协议为运行在不同端系统上的应用进程之间提供逻辑通信功能;应用层进程使用传输层提供的逻辑通信功能而无需考虑实现通信的物理基础设施的细节;

值得注意的是,传输层协议是在端系统中实现的而不是在路由器中实现的。传输层接收来自应用层的报文并通过添加传输层首部以生成传输层报文段。在生成过程中可能会对来自应用层的报文加以分割;然后在发送端系统中,运输层会将这些报文段交给网络层;网络层将其封装成网络层分组,也被称为数据报,然后向目的地发送。路由器不会检查封装在数据报中的传输层报文段的字段;在接收端,网络层从数据报中抽取传输层报文段,并将其交给传输层,传输层接收到报文段后,使该报文段中的数据被接收进程所使用。

网络应用可以使用多种传输层协议,因特网有两种传输层协议,即TCP和UDP,不同的传输层协议提供不同的运输层服务;

传输层和网络层的关系

值得注意的是,网络层提供主机之间的逻辑通信而传输层为运行在不同主机上的应用进程提供逻辑通信;运输层协议只工作在端系统中。在端系统中,传输层协议将来自应用进程的报文移动到网络边缘即网络层,反过来也从网络层接收这些报文段;传输层对报文段如何在网络核心传输并不做干涉;事实上中间路由器既不处理也不识别传输层加载应用层报文上的任何信息;

运输层协议能提供的服务常常受制于底层网络层协议的服务类型,如果网络层协议无法为主机之间的通信提供时延和带宽保证的话,运输层协议也就无法为进程之间发送的应用程序报文提供时延或者带宽保证;

因特网传输概述

因特网为应用层提供了截然不同的两种传输层协议:UDP(用户数据报协议)它提供一种不可靠、无连接的服务;另一种是TCP,它提供可靠的,面向连接的服务;运输层分组也被称为报文段;

简单了解一下因特网的网络层,网络层协议有一个名字即IP,即网际协议。IP为主机间提供逻辑通信,IP的服务模型为尽力而为交付服务(best-effort delivery service)这意味着IP尽最大的努力在主机间交付报文段,但是不做任何保证。它不保证报文段的交付、不保证报文段按序交付、不保证报文段中数据的完整性;即IP提供一种不可靠的服务;每台主机都需要有一个网络层地址,即IP地址。

UDP和TCP最基本的责任就是将IP提供的主机间交付服务扩展到不同端系统上两个个进程之间的服务。这也被称为传输层的多路分解和多路复用;UDP和TCP通过在传输层首部添加差错检查字段来提供完整性检查。进程到进程之间的数据交付和差错检查是最低限度的两种传输层服务,也是UDP可以提供的仅有的两种服务。UDP和IP一样,也是不可靠服务;

TCP提供额外的服务,首先它是一种可靠数据服务,这意味着TCP协议保证数据的按序、完整地从发送端应用进程发送到接收端应用进程;TCP通过序号、确认、定时器以及流量控制来将IP的不可靠数据传输转换为可靠数据传输;其次,TCP提供拥塞控制,拥塞控制与其说是一种提供给应用程序的服务,不如说是一种提供给整个网络的服务,因为整个网络都将因为拥塞控制而受益;不太严格地说,拥塞控制力求为每一个经过一条拥塞网络的连接提供平等的共享网络链路带宽,从而避免一条TCP连接用过多的流量来淹没通信主机之间的链路和设备;拥塞控制是通过调节发送进网络的的流量速率来做得到;UDP不提供拥塞控制;

一个提供可靠数据传输和拥塞控制的协议一定是一个复杂的协议,我们接下来会探求这样的一个协议实现,但是在这之前,需要了解多路复用和多路分解这个概念;

多路复用和多路分解

有前面的介绍,我们可以知道,传输层将网络层提供的面向主机的逻辑通信扩充为面向不同应用进程的逻辑通信,并且这一过程称为多路复用和多路分解;值得注意的是,多路复用和多路分解是每个计算机网络都需要的;

实际上,传输层和应用程序进程之间通过Socket(套接字)关联,这样通过Socket就可以区别同一主机上的不同应用进程,从而传输层提供服务变为可能;传输层从同一台主机上的不同Socket接收数据的过程称为多路复用;传输层向同一台主机上的不同Socket传输数据的过程称为多路分解;为了实现多路复用和多路分解,我们需要标志套接字,并将相关信息添加到报文段中。

实际上,每个套接字都有一个唯一的ID,被称为端口号;而在传输层接收到来自应用程序的分组并通过添加传输层首部而形成报文段的过程中,该端口号被写入;端口号大小在0-65535之间,其中0-1023属于周知端口号,它们为特定的Socket而拥有;

无连接的多路复用与多路分解

需要注意的是,在创建Socket的时候,是由传输层为之分配端口号;一个UDP套接字是由一个目的IP地址和目的端口号即二元组来标志的;如果两个UDP报文段有不同的源IP地址或者源端口号,但是有相同的目的IP和目的端口号的话,它们将通过同一个Socket到达同一个应用程序,除非TCP报文段携带了初始创建连接的氢气;

面向连接的多路复用与多路连接

TCP协议中的Socket是通过一个四元组来标记的:(源IP地址,源端口号,目的IP地址,目的端口号);两个具有不同源IP地址或者源端口号,但有相同的目的IP地址和目的端口号的TCP报文段将通过两个不同的Socket进入同一应用进程;这也表示,一个应用进程可以关联多个Socket,而一个Socket将只关联一个应用进程;常常,这样的对应关系是通过线程来实现的:一个进程有多个线程,而每个线程关联了一个Socket;这样做可以提高服务器性能嘛;

实际上,传输层就是根据这些信息来实现多路分解的;而这些信息是在多路复用的时候被放置在报文段中的;

无连接运输UDP

一个不提供不必要服务的传输层协议是什么样的呢?什么都不做?那是不行的,我们必须做一些什么!前面提到过,差错检查和进程到进程的数据交付是传输层协议必须提供的功能,事实上,UDP的确做到了这些,并且。。只做了这些。它几乎没有对IP增强别的东西;因为在发送报文段之前,发送方和接收方的传输层实体之间没有握手,所以UDP也被称为无连接的;

DNS是一个使用UDP的应用层协议例子;UDP在接收到来自Socket的数据时,UDP为该报文添加首部字段(源和目的端口号,以及其他两个小字段),然后将报文段交给网络层,网络层通过IP协议尽力地将该报文段交付,至于结果,尽力就好;当DNS客户端等待不到对该查询的响应时(有可能网络层将其丢失了)则会向其他Name Server发送查询请求,要么就通知应用程序,做不到;

既然TCP提供了可靠数据传输,并且提供了拥塞控制,为什么人们还需UDP呢?存在即合理,你觉得不合理是因为你还不能理解,但并不能改变其合理或者存在这一既定事实,除非你做些什么。事实上,有些应用很适合UDP(因为没有连接过程啊,因为不会受拥塞控制的调节啊,更自由呗);UDP有以下好处:

  1. 关于何时、发送什么数据的应用层控制更为精细:这是因为一旦应用程序将数据交给UDP,UDP就会打包将其发送给网络层,不会受到传输层的调节,这在一些实时应用中比较实用;当然,应用程序还可以通过UDP+自主开发一些功能的模式来扩展UDP。
  2. 无需建立连接:所以就不会引入额外的时延。这也可能是DNS使用UDP而不是TCP的主要原因,如果使用TCP的话,DNS服务将会慢很多;HTTP使用TCP的主要原因是对TCP的可靠性的依赖超过对速度的要求;
  3. 无需维护连接状态:TCP为了实现可靠数据传输和拥塞控制需要在端系统中维护一些参数,这些参数包括:接收和发送的缓存、拥塞控制参数、确认号和序号;这些参数信息都是必须的;而UDP因为不建立连接,所以自然也就不需要维护这些状态,这就减少了时空开销;
  4. 分组首部更小:TCP有20字节的首部开销,而UDP只有8字节;

这些应用程序使用了TCP作为其传输层协议:电子邮件、远程终端访问、Web、文件传输;这些应用通常使用UDP作为其传输层协议:远程文件服务器、网络管理(因为这里应用即便在网络处于拥塞的情况下仍要工作,所以UDP更为合适)、路由选择协议和名称转换(DNS);这些应用两个都有使用:流式多媒体、因特网电话等多媒体应用;这些应用对实时性的要求较高同时对可靠性的要求又不是很高,所以既可以使用UDP也可以使用TCP协议。

不过在UDP之上运行多媒体应用是有争议的,因为UDP没有拥塞控制协议,所以其对网络有很大的威胁性:大量的UDP流量将使网络过度拥塞而造成TCP连接几乎无法传输数据,并且因为网络拥塞,所以应用又有着较高的丢包率,而因为丢包率UDP很有可能继续发送数据,由此使得网络效率低下。也有研究人员提出一些新的机制,使得所有数据源,包括UDP源执行自适应的拥塞控制来解决这一问题;

需要注意的是,使用UDP仍然可以实现可靠数据传输,只不过这一部分功能需要在应用程序中自主开发;将可靠性直接构建于应用程序中,将使其既可以可靠地传输数据又可以避免受制于TCP的拥塞控制(传输速率的控制);

UDP报文结构

UDP首部只有4个字段,每个字段占用两个字节,分别是:源端口号、目的端口号、长度和校验和;其中,长度表示包含首部在内的UDP报文段长度,以字节为单位;校验和字段用来计算报文段在传输的过程中是否出现了差错;一种常见的校验和的计算方法是:发送方将前三个字段做按位加运算,然后将其取反作为校验和;然后接收方对所有四个字段(每个字段16位)进行求和,如果没有出现差错,则最后的结果全是1,否则就表明出现了错误;出现错误的原因可能有:传输链路上数据受到干扰、数据存储在中间路由器的时候,出现了错误;

UDP作为传输层协议,提供的差错检测功能很有可能和底层协议提供的相似功能产生冗余;但是,这是必须的,因为由于不能保证源和目的地之间所有链路都提供差错检测功能,即便数据在链路上正确传输,也无法保证其在中间路由器的内存中不发生错误;所以要实现端到端的差错检测,就必须在传输层协议中实现该功能;这一原则在系统设计中被称为端到端原则:“因为某一功能必须在端到端实现,与在较高层次提供这些功能的代价相比,在较低层次上设置的功能可能是冗余的,或者根本是没有用的”;

IP作为网络层协议,可以运行在任何第二层协议上,所以运输层提供差错检测也是必须的;UDP可以检测差错,但是无法恢复差错,能做的除了将其丢弃外,便是将其交给应用程序然后给出警告;

可靠数据传输原理

可靠数据传输的问题,不仅在传输层需要考虑,在链路层以及应用层都需要考虑这个问题,所以了解一些其基本原理是必要的,特别是在详细了解TCP中为实现可靠数据传输而采取的。许多原理;

可靠数据传输为上层实体提供的服务抽象是:数据可以通过一套可靠的信道进行传输,借助于可靠信道,传输数据就不会受到损坏或者丢失;并且所有数据都可以按照其发送顺序进行交付。而这正是TCP向调用它的应用所提供的服务模型;

实现这种抽象服务是可靠数据传输的责任,但是因为可靠数据传输的底层协议可能是不可靠的,所以这项任务有一点困难;

单方向的可靠数据传输流程大概是这样的:可靠数据传输->不可靠数据传输->不可靠的传输信道->可靠数据接收->上传Data;

一个可靠数据传输协议,将要面对以下问题:分组丢失、分组损坏到达、分组乱序到达;

  1. 分组损坏到达问题,可以通过校验和来判断是否发生损坏,如果损坏可以采用两种方法:一种方法是由接收方就地恢复,这要求校验和字段包含足够的信息;另一种方法是接收方通知发送方再次传输该分组,一般来说采用这种方法;这样就需要有一个方法供接收方和发送方就是否正确传输某一分组进行沟通,从而引入了ACK和NAK分组;
  2. 分组乱序到达问题,可以通过对分组编号来解决,这样接收方就可以对下一个到达的分组产生“期望”;如果发生了乱序,接收方可以有两种不同的解决方法,第一种,是简单丢弃该分组,并告诉发送方自己的期望;第二种是将该分组缓存下来,然后等所有该到的分组都到了后,一起将数据向上层协议提交;第一种方法看起来就很愚蠢:都正确接收到了数据,何必要丢弃呢?第二种方法比较常用;
  3. 分组丢失问题,可靠数据传输采用“ACK/NAK”机制来解决:当发送方发出编号为0的分组,接收方需要向发送方回发一个ACK分组,表示接收方正确收到该分组;当分组损坏或者丢失或者超时的时候,接收方将向发送方发送对上一次接收到的分组的ACK,暗示发送方,该到的没到;

总体上来说,可靠数据传输面临的问题就是这么多,对不同问题所采取的解决方法不同导致了不同的可靠数据传输方案的实现策略;而总体上用的解决方法有:定时器、序号、肯定确认、否定确认、校验和、窗口和流水线;窗口和流水线将在下面提到;

我尝试着理清它们的关系:

根据发送方一次发送的分组数目,可以将可靠数据传输协议的工作模式分为停等协议和流水线;

停等协议

所谓停等协议是指发送方发送完分组A后,需要等待接收方对分组A的反馈分组,如果收到肯定分组,那么就发送下一个分组;如果收到否定分组,那么就重新发送当前分组;值得注意的是,有些协议中只有肯定分组,肯定分组包含一个分组号K,表示标号为K的分组已经收到,发送方可以发送标号为K+1的分组;如果发送方此时等待的正好是对K的确认分组,那么发送方就会发送标号为K+1的分组;如果发送方接收到对K-1的确认,表示接收方并没有收到编号为K的分组,需要重发;另外,停等协议中只需要一个定时器用来监听超时事件;

停等协议的一个明显问题就是效率低下,甚至可以作为网络协议限制网络硬件发挥性能的例子;

流水线协议

所谓流水线模式是指发送方连续发送M个分组,然后等待接收方对这M个分组的确认情况;如何处理发送和收到的分组,这要看流水线模式的具体策略,因为这涉及到差错恢复方法,而在流水线模式下,差错恢复方法有两种,一种是回退N步,一种是选择重传;

回退N步

在回退N步中,发送方维护一个N——窗口大小和一个base——发送方期待收到的最小待确认分组序号,同样也是窗口的起点,还有一个next Sequence变量,表示上层需要发送分组时,可以使用的序号。这样全部序号就被划分为0-base-1,这一部分的分组是已发送且收到接收方确认的分组,base-next Sequence-1这一部分的分组是已发送但是尚未收到确认的,其中base是尚未收到确认的最小序号;next-1到base+N-1表示当前发送方可以使用的序号,表示一种发送能力;当发送方收到确认号为base的确认分组后就会向前移动窗口,所以回退N步也被称为滑动窗口协议;

这是发送方需要维护的数据,同时发送方需要响应的事件有:上层调用、收到ACK、超时事件;

  1. 上层调用:检查next Sequence是否在窗口之内,如果在,这说明发送方还有发送能力,发送之;
  2. 收到ACK:回退N步策略对序号为n的分组采取累积确认的方式,即当收到序号为n的ACK时,表明序号小于等于n的分组全部到位;发送方收到的ACK毕竟来自接收方,收到ACK的情况还得看接收方如何发送;
  3. 超时事件:如果发生超时事件,那么发送方会重发所有已发送但是未确认的分组,即分组号在base和next sequence-1之间的所有分组;这也是为什么叫“回退N步”,如果收到一个ACK,则定时器会重行启动;如果没有待确认的分组,定时器将被终止;

在接收方,如果到达分组的序号为n且该分组是按序到达,那么发送ACK,这就导致发送方移动窗口;如果不是按序到达,那么接收方丢弃所有失序分组;丢弃一个正确接收的失序分组可能会导致更多的重传;

选择重传

回退N步协议存在一个问题就是当窗口和带宽的时延都较大时,单个分组的差错可能会引起GBN重传大量的分组,然后许多本来不用重传的分组会充斥在信道中,造成资源浪费;选择重传就是让发送方仅重传那些丢失和受损的分组而避免不必要的重传;

发送方需要响应的时间仍然有三个:

  1. 上层调用:基本上同GBN一致;
  2. 收到ACK:如果该分组号在base-next sequence-1之间,将其标记为已到;如果等于base,则移动窗口到最下的待确认的分组序号处;在GBN中,接收方根本不会收到非base的ACK,但是怎么收,还的看怎么发;
  3. 超时事件:同GBN不一样的是,选择重传需要为每一个分组建立一个定时器,如果某个已发送但未被确认的分组超时,发送方重发该分组;

接收方将确认一个正确接收的分组而不管其是否按序;失序的分组被缓存,直到形成连续数据后将其提交给上层;值得注意的是,如果接收方收到了已经确认的分组,则说明确认ACK丢失,或者时延太长,接收方和发送方沟通不及时;这也表明了关于那些分组到位了,那些分组还没到位,接收方和发送方有着不一样的视图;

另外还需要注意的是,序号的重用问题,如果在分组中序号字段的位数为k,那么最大的序号为2^k-1,所以有可能不同分组同时占用一个序号,为了避免这种情况,需要做的是控制分组的生命周期,总之,问题是存在的,解也是存在的;

面向连接的TCP

TCP连接

TCP协议之所以被称为是面向连接的协议,是因为在一个应用进程可以向另一个应用进程发送数据前,这两个进程将首先“握手”,即它们必须交换一些预报文段,已建立对关于数据传输的参数的共识;作为TCP连接建立的一部分,通信双方都将初始化与TCP连接的许多相关变量;

TCP的连接,并不是一条像电路交换网络中端到端的TDM、FDM电路,而不是一条虚电路;其连接状态被端系统所维护而中间路由器完全忽略了该协议,中间路由器看到的只是数据,也就是说,TCP只运行在端系统之上;所以,TCP连接更像一种状态而不是物理的、实际的连接;

TCP提供全双工服务,并且是点对点的,数据从A到B的同时,也能从B到A;TCP协议无法提供“多播”服务,一条TCP连接只关联一个发送方和接收方(当然,发送方也是接收方);

对于TCP建立过程中的“握手”阶段,需要明白的是,手一共握了三次,前两次报文段不承载“有效负载”,第三次握手的时候,报文段是可以装载“有效负载”的;这个过程是这样的:通信的发起方首先发送一个特殊的TCP报文段给接收方,这是第一次握手;接收方收到该报文段后,对该报文段进行响应,此为第二次握手;发送方接收到响应报文段后,发送第三个报文段,其中包含了有效负载;因为TCP建立的过程,一共发生了三次握手,所以该过程也被称为“三次握手”;

当TCP连接建立后,两个应用进程就可以发送数据了。应用程序将要发送的数据通过Socket传递给TCP,TCP将数据引导到该连接的发送缓存,发送缓存大小是在三次握手的过程中确定的;之后TCP将时不时从该缓存中拿出数据进行发送,一个有趣的事情是,TCP规范中没有规定TCP应该在何时发送缓存里的数据,描述为“TCP应该在它方便的时候以报文段的形式发送数据”;TCP每次可以从缓存中发送的最大数据长度称为MSS(Maximum Segment Size)。一般来说,MSS+TCP/IP首部的长度要小于等于链路的MTU(即链路层最大帧长度Maximum Transport Unit)而以太网和PPP的MTU都等于1500字节,TCP/IP的首部通常为40字节,所以MSS一般来说为1460字节;

TCP为每块客户数据加上TCP首部后就形成了一个个TCP报文段;这些TCP报文段被交给网络层,然后被发送到网络中;当TCP报文段到达接收端时,便进入了接收端的缓存,等待被应用程序读取;

TCP报文段结构

TCP报文段结构,从整体上来说由首部+数据字段组成;其中数据字段来自应用层,其长度不能大于MSS;首部的常规长度为20字节,但是值得注意的是,TCP首部是可变长的;TCP首部是以32比特为单位组织的,其结构组成如下图:

  1. 源端口号和目的端口号

    这两个数据用于TCP的多路复用和多路分解;分别为16位;

  2. 序号

    该数据被用于实现可靠数据传输之按序到达,在一个TCP连接中,算是一个报文段的id,同时该id还指示了其所承载的数据的位置信息;占32位;

  3. 确认号

    该数据表示接收方已经正确接收的报文段的序号,在流水线的差错恢复方案里,不同的恢复策略有不同的意义:回退N步里,当发送方接收到对K的确认号时,表示所有序号小于K的报文段均已到达;而在选择重传里,则仅表示序号为K的报文段被正确接收;

  4. 首部长度

    TCP的首部是可变长的,所以该字段表示报文段的首部长度,也揭示了应用数据的开始位置;该字段以32比特为单位,占4比特(所以表示TCP首部的最大长度为31*32比特,大小需要自己算啦~);

  5. 选项字段

    该字段用于在发送方和接收方之间协商MSS的大小,在高速网络环境下,也可用于调节窗口大小;

  6. 标记字段

    ACK位表示确认号字段的里的值是否有效,如果ACK被置位,那么该报文段就对确认号所指示的报文段进行了确认;

    RST、SYN和FIN位用于TCP的连接和拆除;

    PSH被置位时,指示接收方应该立即将数据交给上层;

    URG被置位时表示报文段里存在着发送端的上层实体置为紧急的数据;紧急数据的最后一个字节由16位紧急指针指出。当紧急数据存在并且给出了指向紧急数据尾指针时,TCP必须通知接收端的上层实体;

    然而,实际上,PSH、URG和紧急数据指针在实践中并没有被使用;标记字段一共6比特;

  7. 窗口大小

    该字段用来指示报文段发送方窗口的大小——窗口大小在一定程度上指示了缓存的空间大小;

序号和确认号

TCP报文段中两个重要的字段是确认号和序号;这两个字段是TCP实现可靠数据传输的重要部分;TCP将数据看作是一个无结构、有序的字节流;值得注意的是,TCP的序号是基于传输的字节流之上,而不是报文段的序列之上;也就是说,来自应用层的数据被TCP包装在多个报文段中,其中第2个报文段的序列号不是2,而是1001,如果MSS为1000。关于确认号,如果采取回退N步策略,那么TCP采用一种累计确认的方法,前面已经提到过,这里就不赘述;一条TCP连接可以采取任意数字作为初始序号,这样可以减少将那些残存在网络中的报文段误认为是新建连接的报文段(新旧连接恰巧采用了相同端口);

总体来说,一个报文段的序号就是该报文段数据字段首字节的序号;确认号就是接受主机正在等待接收的数据的下一个字节序号;值得注意的是,服务端对接收端发来的报文段的确认被装载到一个从服务端发往到接收端的报文段中,这种确认被称为“捎带”;

往返时间的估计与超时

TCP使用超时重传机制来处理报文段的问题;虽然这在概念上很简单,但是一个问题是,如何设置超时时间?如何设置超时时间,取决于网络的状态,所以需要做的是估计网络的状态。TCP使用一种Sample RTT的方法来估计RTT。Sample RTT就是从某报文段发出到收到对该报文段的确认之间的时间量。大多数TCP的实现是在某个时刻做一个Sample RTT测试。TCP并不为已经重发的报文段做Sample RTT测试,它只为传输一次的报文段测量Sample RTT。

TCP一般来说通过Estimated RTT=(1-a)Estimated RTT+a*Sample RTT来计算因路由器的拥塞和端系统负载变化所导致变化的RTT。a一般取1/8;因为Estimated RTT表示最近的网络状况,所以其理应得到较大的权值;这种方法也被称为指数加权移动平均;

除了估计RTT外,计算RTT的变化也是ok的,DevRTT =(1-b)DevRTT+b*|Sample RTT-Estimated RTT|;其中b的推荐值为0.25;当Sample RTT变化较大的时候,DevRTT的值较大,当Sample RTT变化较小的时候,DevRTT就较小;

TCP是如何考虑超时时间的呢?该时间因略大于测量的RTT,不易过小——容易引起不必要的重传,也不易过大——网络对于报文段丢失情况的反应就会变慢;最后TCP采用了如下计算方式:Timeout Interval=Estimated RTT+4*Dev RTT;

可靠数据传输

IP协议提供的是尽力而为的服务:不保证不丢失、不保证按序到达、不保证没有损坏,TCP协议在IP协议之上,提供可靠数据传输,从而保证一个进程从其相关联的缓存中读取的数据和另一端进程发送的数据是一致的;TCP使用超时重传和冗余确认技术来处理超时、丢失等情况;使用确认、序号等技术来保证按序到达;使用校验和来检验是否报文段在传输过程中是否发生了错误;

超时时间加倍

在大多数TCP实现中,当发生超时事件时,超时时间并不是从Estimated RTT和Dev RTT推算出来而是直接将超时时间设置为原来的两倍;然而,每当定时器在另两个事件(收到ACK和接收到上层应用数据)发生时,新的超时时间将由上面提到的两个值计算出来;实际上,这是一种形式受限的拥塞控制;

快速重传

响应超时事件,然后重传尚未收到确认的报文段,但是,当超时时间过长的时候,会显著增加端到端的延迟;一种可行的方法是对冗余ACK的的检测;在理解冗余ACK之前,需要先看一下接收方为什么会发送冗余ACK。接收方接收到某个报文段时,会检查该报文段是否是按序到达,如果不是,那么接收端会发送对已经收到的最后一个连续报文段的确认,所以如果发送方收到冗余ACK,说明有多个报文段到达了接收端,但不是接收端所期望的——这意味着,很有可能发生了丢失。所以发送方可以在定时器过时之前快速重传所丢失的报文段;

是回退N步还是选择重传?

首先,我们需要明白的是,TCP采用了累计确认的机制,也就说,如果接收方正确接收了某一失序到达的分组,那么接收方发送的ACK将是对最后接收的按序到达的分组的确认,而不是对刚刚接收的分组的确认;当然,许多TCP实现都会缓存失序的分组;那么问题来了,发生超时事件后,GBN将重传所有待确认的分组,而不是丢失的分组;而选择重传会好很多;

对TCP提出的一种修改意见是所谓的选择确认——即接收方对失序到达的分组也会确认,当该机制和重传机制相结合使得TCP更像选择重传,于是TCP的差错恢复协议最好被分类为GBN和SR协议的混合体;

流量控制

流量控制是一个速度匹配服务:TCP连接的发送方和接收方都各自维护一个缓存,因此两者的数据交换应该在一个合理的速度范围内:不让对方发生数据溢出;TCP为它的应用程序提供了这种服务:流量控制服务。虽然流量控制和拥塞控制所采取的动作非常相似,但是它们的目的很明显并不同。在接下来的讨论中,我们将假设TCP是这样实现的,即TCP接收方丢弃失序到达的报文段;

在TCP首部中有一个窗口大小字段,TCP连接的双方通过该字段来向对方表明自己的窗口大小,即缓存空间的大小;同样,在TCP连接的两端,各自维护着相关的变量:last Sent、last Acked;在发送方,这两个变量之间的分组就是已经发送但是尚未确认的分组;而在接收方,last Read表示应用进程下一次读取的数据,last Revd表示最后纳入缓存的报文段编号(注意,我们讨论的前提是TCP会将失序到达的报文段丢弃哦~);通过这些变量以及报文段首部中窗口大小字段,我们就可以对发送速度做一些控制:在发送方last Sent-last Acked应该小于等于接收方的窗口大小;在接收端A=last Received-last Read就是已经使用的空间大小,所以窗口大小=buffer-A;

实际上,流量控制是一种对发送方的限制措施吧,但是因为TCP是一种全双工的协议,所以接收方同时也是发送方;

对了,还有一个问题就是,如果接收方的窗口大小为0,那么发送端该如何处理呢?一个需要注意的事实是,接收方在没有ACK或者数据要向发送端发送的时候,是不会通知发送方其窗口大小已经改变,即如果应用程序读取了缓存中的数据,发送方是不会知道的,除非它向接收方发送了数据,而发送方对其进行了确认;实际上,发送方也是这么做的!当接收到窗口大小为0的报文段后,发送方会向接收方间隔发送只有一个字节的数据;这样,双方的通信就不会被中断啦;

连接管理

TCP连接管理包括如何建立和拆除一条TCP连接;TCP的建立很重要,因为它是网络时延的重要组成部分;当然,TCP连接的建立过程被称为“三次握手”而拆除过程被称为“四次挥手”——形象地描述了两个过程中的数据交换情况;

建立过程

  1. 客户端TCP发送SYN报文段,所谓SYN报文段就是SYN位被置1的特殊报文段,它不包含应用层数据;同时会将一个随机的序号放置于序号字段;
  2. 包含SYN报文段的IP数据报到达服务端,服务端发现这是一个SYN报文段后,将视情况决定是否同意该建立TCP的连接请求,如果同意,那么服务端将为之分配必要的缓存空间和变量,然后准备发送ACK报文段,首先服务端读取其中的序号,将该序号+1后设置为ACK报文段的确认号,然后SYN位被置1,然后服务端选择自己的初始序号,将其放到序号字段;允许建立TCP连接的报文段也被称为SYNACK报文段;当然,这里还是没有应用层数据;
  3. 在收到SYNACK报文段后,发起端也将分配缓存和变量,同时客户主机将向服务器发送另外一个报文段,该报文段对SYNACK进行确认,此时SYN位被置0;同时,该报文段可以携带客户数据;

以上就是TCP连接建立的过程,因为一共发送了三个分组,所以也被称为“三次握手”;

拆除过程

  1. (客户端关闭)客户TCP发送一个特殊报文段到服务器,该报文段中FIN位被置1;
  2. 服务器收到后向发送方回送一个ACK报文段;
  3. (服务器端关闭)服务器端发送FIN报文段给客户端;
  4. 客户端发送ACK对该报文段进行确认;

然后,TCP连接就拆除了。

拥塞控制原理

拥塞原因与代价

计算机网络拥塞的原因是因为网络中的分组太多,而链路带宽和路由器缓存容量都是有限的;

  1. 当分组的到达速率接近链路容量时,分组将经历巨大的排队时延;
  2. 发送方必须执行重传已补偿因为缓存溢出而丢弃的分组;
  3. 发送方遇到大时延时所进行的不必要重传呼应器路由器李永奇链路来转发不必要的分组。
  4. 当一个分组沿着一条路径被丢弃时,每个上游路由器用于转发该分组到丢弃该分组而使用的传输容量最终被浪费掉了;

拥塞控制方法

总体来说,我们可以更具网络层是否为传输层拥塞控制提供了显式帮助来区分拥塞控制方法:端到端拥塞控制和网络辅助拥塞控制;

在端到端拥塞控制方法中,网络层并没有向传输层拥塞控制提供显式支持,即便网络中存在拥塞,端系统也必须通过对网络行为的观察(如分组丢失与时延)来判断;TCP必须通过端到端的方法解决拥塞控制,因为IP层不会像端系统提供有关网络拥塞的反馈信息。TCP报文段的丢失(超时或者收到3次冗余确认而得知)被认为是网络拥塞的一个迹象,TCP将相应地减小窗口长度;

在网络辅助的拥塞控制方法里,网络层会向发送方提供关于网络中拥塞状态的显式反馈消息;比如使用一个比特位来指示网络是否拥塞;拥塞信息从网络反馈到发送方一般有两种方式,其中直接反馈信息可以由网络路由器发送给发送方,这种方式的通知通常采用一种拥塞分组的形式;第二种形式的通知是路由器标记或者更新从发送方到接收方的分组中的某个字段来指示拥塞的产生,然后由接收方向发送方通知该网络发生了拥塞。

TCP拥塞控制原理

TCP必须使用端到端的拥塞控制而不是网络辅助的拥塞控制,因为IP并不会向端系统提供显式的网络拥塞反馈;TCP所采用的方法是让每一个发送方根据其所感知的网络拥塞程度来限制其能向连接发送流量的速率;如果TCP判断网络通畅,那么它会提高发送速率,如果TCP判断网络拥塞,那么它会限制发送速率;需要解决三个问题:TCP如何限制发送速率?TCP如何感知网络拥塞程度?TCP该以何种算法改变其发送速率?

我们知道,TCP连接的双方都维护着两个窗口,其中一个是作为发送方的窗口,也被称为拥塞窗口,它对发送方能向网络中发送流量的速率进行了限制,last sent-last acked<=min{cwnd,rwnd};另一个自然是作为接收方的接收窗口。

我们假设,发送方可以在RTT时间范围内连续发送cwnd个字节的数据,所以发送速率即为cwnd/RTT;发送方通过调整窗口大小来对发送数据的速率加以控制;(回答如何控制速率);

我们将TCP发送方的丢包事件定义为:要么超时,要么收到接收方的3个冗余ACK;如果网路拥塞,那么网络中的路由器就会发生缓存溢出,进而导致数据报被丢弃,然后就会引起发送方的丢包事件;此时,TCP发送方就可以认为TCP连接出现了拥塞;(回答如何感知网络拥塞程度);

另外,TCP将接收方发送的ACK视为网络通畅的标志,如果ACK到达的速率较高,那么TCP的拥塞窗口就会以较高的速率扩大,如果ACK到达的速率较慢,那么TCP拥塞窗口的增加速度也会较慢;因为TCP使用ACK对拥塞窗口做出调节,所以也别称为自计时的;

TCP发送速率过高,网络就很容易拥塞;TCP发送方如果过于谨慎,那么就无法充分利用网络的带宽;所以TCP如何设置自己的发送速率,才能使得网络不会拥塞而且还充分利用带宽呢?关于这个问题,TCP使用下列指导性原则回答这些问题:

  1. 一个丢失的报文段意味着拥塞,因此当丢失报文段时应当降低TCP发送方的速率;
  2. 一个确认报文段指示该网络正在向接收方交付发送方的报文段,因此,当收到对先前报文段的确认时,可以增加发送方的速率;
  3. 带宽检测;TCP调节器传输速率的策略是增加其速率以响应到达的ACK,除非出现丢包,此时才减少发送速率;以为网络中没有明确的拥塞控制状态信令,ACK和丢包事件充当了隐式信号;

以上为TCP拥塞控制的概述,接下来就是广受赞誉的TCP拥塞控制算法,该算法包含三个主要部分:慢启动、拥塞避免、快速恢复;慢启动和拥塞控制是TCP的强制部分;两者的差异在于对收到的ACK做出反应时增加cwnd长度的方式;快速恢复是推荐部分,对于TCP发送方并非是必须的;

慢启动

TCP连接在开始的时候,其cwnd常设置为一个MSS,然后在慢启动状态每收到一个ACK,cwnd就增加一个MSS;这样的话,在慢启动阶段,发送速率是指数增加的(1,2,4,8…);

何时结束这种指数增长?有三种情况:发送了超时、发生了冗余ACK以及cwnd达到ssthresh。ssthresh是慢启动阈值的速记;在慢启动阶段,如果发生了超时事件,那么ssthresh就被设置为当前cwnd的一半,然后将cwnd置为1;当cwnd逐步增加到ssthresh时,再翻倍增加cwnd就有一点鲁莽了,所以此时TCP结束慢启动,进入拥塞避免模式。在拥塞避免模式里,TCP将更谨慎地增加cwnd;如果收到冗余ACK,那么TCP会做一次快速重传,然后进入快速恢复阶段;

拥塞避免

一旦进入拥塞避免状态,cwnd的值大约是上次遇到拥塞时的一半,所以TCP在每个RTT中,只将cwnd增加一个1个MSS大小;也就是说在拥塞避免阶段,cwnd是线性增加的;

当出现超时时,TCP将cwnd设置为1,然后将ssthresh更新为cwnd的一半;当收到冗余ACK时,TCP将cwnd减半,然后将ssthresh置为cwnd值的一半,并且进入快速恢复状态;

快速恢复

在快速恢复阶段,对于引起TCP进入该状态的缺失报文段,每收到一个ACK,cwnd增加一个MSS;最终,当对丢失报文段的一个ACK到达时,TCP降低cwnd后进入拥塞避免状态;如果出现超时事件,快速恢复在执行如同慢启动和拥塞避免中相同动作后,进入慢启动状态;

猜你喜欢

转载自blog.csdn.net/slx3320612540/article/details/82832583
今日推荐