计算机网络运输层

本文巨长且干货极少,阅读请谨慎!!!!

1 运输层协议概述

1.1 为什么需要运输层?

和之前介绍的层次一样,在开始之前,我们需要先知道为什么需要运输层

我们知道现代操作系统,都是支持"多任务"的操作系统。“多任务"简单的说就是操作系统可以同时运行多个任务,比如你可以一边在用浏览器上网,一边打开网易云听歌,一边用Word赶作业。

那么操作系统如何实现这种”多任务“呢?答案就是操作系统轮流让各个任务交替执行,但是因为CPU太快了,给我们的感受就是所有任务都在同时执行。对于操作系统而言,一个任务就是一个进程(Process),

这里提到进程,我们知道,计算机网络体系结构中的物理层数据链路层以及网络层它们要解决的问题是在网络互联的情况下,如何实现主机到主机的通信

然而,真正在计算机网络中进行的通信是位于通信两端主机中的进程之间的通信,是一台主机中的一个进程和另一台主机中的另一个进程在交换数据(也就是通信)。

IP协议能把分组送到目的主机,但是这个分组只是停留在目的主机的网络层而不知道交付给主机中的哪一个进程,而运输层就是来解决交付给哪个进程的问题

从运输层的角度看:

通信的真正端点并不是主机而是主机中的进程

比如说在一台主机上经常有多个进程同时分别和另一台主机上的多个进程通信。例如,你在使用浏览器查找某网站的信息时,你的主机应用层运行浏览器客户进程。如果你在浏览网页的同时,还使用了电子邮件给网站发送反馈意见,那么你的主机的应用层还要运行电子邮件的客户进程。

1.2 运输层的一些功能

1.2.1 复用和分用

如图5-1 主机A的应用进程AP1和主机B的应用进程AP3通信,与此同时,主机A应用进程AP2也和对方的应用进程AP4通信。比如说你和朋友在微信聊天,这时突然你要传输一个大文件给朋友,微信实现不了,所以你打开了QQ向朋友传大文件,QQ传输文件的同时,你继续和朋友聊天。

这表明了运输层有很重要的功能复用(multiplexing)分用(demultiplexing)

  • 复用指发送方不同的应用进程都可以使用同一个运输协议传送数据。

  • 分用指接收方的运输层在剥去报文的首部后能够把这些数据正确交付目的应用进程

1.2.2 运输层提供应用进程间的逻辑通信

运输层提供应用进程间的逻辑通信

逻辑通信的意思是:从应用层来看,只要把应用层的报文交给下面的运输层,运输层就可以把这报文传送到对方的运输层(应用层根本不管对方有多远,报文怎么发过去的,下层协议怎么实现的,就只是告诉运输层,你帮我把报文送到某某的运输层,本机运输层就一定可以将报文送到),就好像这种通信就是沿着水平方向直接传送数据,但事实上我们知道,两个运输层之间根本不存在这样一条水平方向的物理链路,报文的传送还是要沿着图中的虚线方向经过多个层次才能传送到目的主机的运输层。

逻辑通信的意思是”好像是这样通信,事实上并非如此“。

这里就可以看出网络层和运输层的区别了:

网络层为主机之间提供了逻辑通信,而运输层为应用进程之间提供端到端的逻辑通信。

如图5-2:

1.2.3 提供可靠和不可靠两种信道

我们知道,数据链路层提供的是无差错传输,不是可靠运输,网络层IP数据报首部中的检验和字段,只检验首部是否出现差错而不检查数据部分。

根据应用程序的不同需求,运输层可以使用两种不同的运输协议,即面向连接的TCP无连接的UDP。 当运输层采用面向连接的TCP协议时,尽管下面的网络是不可靠的,但这种逻辑通信信道就相当于一条全双工的可靠信道。但当运输层采用无连接的UDP协议时,这种逻辑通信信道就仍然是不可靠信道

1.3 运输层的两个主要协议

**(1)用户数据报协议UDP **: User Datagram Protocol

(2)传输控制协议TCP: Transmission Control Protocol

图5-3给出了这两种协议在协议栈的位置

在TCP/IP体系中,根据所使用的协议是TCP或UDP,分别称两个对等运输实体在通信时传送的数据单位TCP报文段(segment)UDP用户数据报

UDP在传送数据之前不需要先建立连接,目的主机的运输层收到UDP报文后,不需要给出任何确认。虽然UDP不提供可靠服务,但在某些应用下,UDP是非常有效的工作方式,因为UDP比较快,效率高。比如说视频通信,在通信中,一些视频帧的丢失我们是可以接受的,但我们要保证的是通信的流畅度和延迟低。

TCP则提供面向连接的服务,在传送数据之前先建立连接,数据传送结束后要释放连接TCP不提供广播或多播服务。正因为TCP要提供可靠的面向连接的运输服务,因此不可避免地增加了许多的开销。

表5-1 给出了一些应用和应用层协议主要使用的运输层协议

1.4 运输层的端口

1.4.1 什么是端口?

前面我们提到了复用分用

  • 复用指发送方不同的应用进程都可以使用同一个运输协议传送数据。

  • 分用指接收方的运输层在剥去报文的首部后能够把这些数据正确交付目的应用进程

举一个日常生活中复用和分用的简单的例子,假定一个学校向其他学校发出的信都由该学校的收发室负责寄出,这相当学校里的每个院系都要复用这个收发室,而当学校收发室收到信后,根据信的收件地址,将信具体送给对应的院系,这叫是分用

运输层上的复用和分用功能也是类似的。应用层所有的应用进程都可以通过运输层再传送到网络层,这就是复用,运输层就相当于收发室,而运输层收到IP层发送给各应用的数据后,必须分别交付指明的各应用进程,这就是分用。那么运输层如何找到对应的各应用进程呢?答案是给应用层的每个应用进程赋予一个非常明确的标志

我们知道,在单个计算机中的进程我们是用进程标识符来标志的。然而不同的操作系统使用的进程标识符格式是不同的,为了使运行不同的操作系统的计算机的应用进程能够实现通信,需要用一种统一的方法来标识进程,而这种方法与特定操作系统必须无关。

但是,把一个特定机器上运行的特定进程,指明为互联网通信上的最后终点还是不可行的,因为我们知道,进程的创建和撤销是动态的,新建一个进程就会给出一个新的进程号,通信的一方几乎无法识别对方机器上的进程,我们往往需要利用目的主机提供的功能来识别终点,而不需要知道具体实现这个功能的进程是哪一个。

解决问题的方法就是在运输层使用协议端口号(protocol port number),也通常成为端口(port)。也就是说,我们不直接把目的主机上的某个进程作为终点(事实上也无法这样做,因为源主机无法识别进程),而只要将所传送的报文送到目的主机上某个合适的端口,至于最后交付给目的进程,就由TCP或UDP来实现。

注意软件端口和硬件端口的区别

在这种协议栈层间的抽象的协议端口软件端口。而在路由器或交换机上的硬件端口是不同硬件设备之间的接口。软件端口相当于是应用层的各种协议进程与运输实体进行层间交互的一种地址

1.4.2 端口的工作原理

当运输层收到IP层交上来的运输层报文时,就可以根据其首部中的目的端口号把数据交付应用层中的目的应用进程

TCP/IP 的运输层用一个16位端口号来标志一个端口,注意的是:端口号只具有本地意义,它只是为了标志本计算机应用层中各个进程在和运输层交互的一个接口。在不同的计算机中,相同的端口号是没有关联的

由此可见,两个计算机中的进程要互相通信,不仅仅必须知道目的主机的IP地址(网络层以下的层需要依靠IP地址找到目的主机),而且要知道对方的端口号(运输层要依据端口号找到目的主机中的应用进程)。

举一个寄信的例子,我们给某人写信,必须写上他的住所地址(为了找到他的家庭地址,相当于IP地址),还要写上收件人的名字(相当于端口号),因为一个住所可能有多个人住,只有写上收件人的姓名,才知道把信给谁。

1.4.3 运输层端口的分类

运输层的端口号分为下面的两大类。

(1) 服务器端使用的端口号:服务器使用的端口号又分为两类,分别是:

  • 熟知端口号系统端口号: 数值为0~1023,LANA把这些端口号指派给了TCP/IP最重要的一些应用程序,让所有用户都知道。如表5-2:
  • 登记端口号:数值为1024~49151 为没有熟知端口号的应用程序使用的,需防止重复。

(2) 客户端使用的端口号:数值为49152~65535。这类端口号仅在用户进程运行时才动态选择,又叫做短暂端口号。这类端口号留给客户进程选择暂时使用,通信结束后,这个端口号就可以供其他客户进程使用。

2 用户数据报协议UDP

2.1 UDP 概述

UDP的主要特点是:

(1) UDP是无连接的 发送数据之前不需要建立连接,当然结束时也不需要释放连接。

(2) UDP使用尽最大努力交付,同样是不可靠交付

(3) UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,不合并,也不拆分保留这些报文的边界。也就是说,应用层交给UDP多长的报文,UDP照样发送,即一次发送一个报文。
如图5-4所示,接收方的UDP对IP层交上来的UDP用户数据报,在去除首部后就原封不动地交付上层的应用进程,也就是说,UDP一次交付一个完整的报文。
这就要求应用程序必须选择合适大小的报文,如果报文太长,IP层在传送时可能要进行分片,这降低了IP层的效率,如果太短,IP数据报的首部的相对长度太大,这也降低了IP层的效率。

(4) UDP没有拥塞控制:网络中出现的拥塞不会使源主机的发送速率降低。对于很多实时应用比如IP电话,实时视频会议要求源主机以恒定的速率发送数据,并且允许在网络发生拥塞时丢失一些数据,但不能有太大的时延,UDP是非常适合的。但这样也有可能引起网络产生严重的拥塞问题。

(5) UDP支持一对一,一对多,多对一和多对多的交互通信

(6) UDP的首部开销小,只有8个字节。

2.2 UDP的首部格式

如图5-5,用户数据报UDP有两个字段:数据字段首部字段。首部字段很简单,只有8个字节,由四个字段组成,每个字段的长度都是两个字节,各字段的意义如下:

  • 源端口:源端口号,在需要对方回信时选用,不需要可用全0
  • 目的端口:目的端口号,这在终点交付报文时必须使用。
  • 长度 UDP用户数据报的长度,最小值为8(仅有首部的情况)
  • 检验和: 检测UDP用户数据报在传输中是否有错,有错就丢弃。

当运输层从IP层收到UDP数据报时,就根据数据报中的目的端口,把UDP数据报通过相应的端口,上交到目的应用进程,如图5-6:

如果接收方UDP发现收到的报文的目的端口不正确,也就是不存在对应于该端口号的应用进程,就丢弃该报文,并由网际控制报文协议ICMP发送"端口不可达"差错报文给发送方。比如我们熟悉的traceroute实现原理就是发送一个不可交付的UDP报文,该报文故意使用一个非法的UDP端口,结果ICMP就返回端口不可达差错报文,达到跟踪路径的目的。

UDP用户数据报首部检验和的计算:

UDP用户数据报首部中计算检验和时,要在UDP用户数据报之前添加12字节的伪首部。所谓“伪首部”并不是UDP用户数据报真正的首部,只是在计算检验和时临时添加到UDP用户数据包前面,得到一个临时的UDP数据报,再根据这个数据报去计算检验和,伪首部仅仅是为了计算检验和。

UDP计算校验和的方法和计算IP数据报首部检验和的方法相似,不同的是IP数据报的检验和只检验IP数据报的首部,而UDP的检验和是把首部和数据部分一起都检验

具体的检验方法是:

在发送方,先将检验和字段置为全0,再把伪首部已经UDP用户数据报看成是由许多16位的字串连接起来的。如果UDP用户数据报的数据部分不是偶数个字节,则要填入一个全0字节。(注意,此字节不发送,只是为了求检验和),然后按二进制反码计算出这些16位字的和,将二进制反码写入检验和字段后,就发送UDP用户数据报。

在接收方,把收到的UDP用户数据报连通伪首部(以及可能的填充全0字节)一起,按二进制反码求这些16位字的和,当无差错时其结果应为全1(求反码时为全0),否则就表明有差错出现。

接收方一遇到差错,就丢弃这个UDP用户数据报(也可以上交应用层,但附上出现了差错的警告)。

如图5-7给出了一个计算UDP检验和的例子

3 传输控制协议TCP概述

好复杂的协议,头大…算了算了一步步学吧~

3.1 TCP最主要的特点

  1. TCP是面向连接的运输层协议:应用程序在使用TCP协议之前,必须先建立TCP连接,传送数据完毕后,必须释放已经建立的TCP连接。

  2. 每一条TCP连接只能有两个端点(endpoint)。每一条TCP连接只能是点对点的。

  3. TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错不丢失不重复并且按序到达

  4. TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据,TCP连接的两端都设有发送缓存接收缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候再把数据发送出去。在接收时,TCP把收到的数据放入缓存,上层的应用进程在合适的时候读取缓存中的数据。

  5. 面向字节流 UDP是面向报文,而TCP是面向字节流的。流(stream)指的是流入到进程或从进程流出的字节序列。含义是:虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交付下来的数据仅仅看成一连串无结构的字节流。TCP并不知道传送的字节流的含义,也不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系。例如,发送方应用程序交给发送方TCP共10个数据块,接收方的TCP可能只用了4个数据块就把收到的字节流交付上层的应用程序,因为应用程序和TCP交互的数据块大小可以不等。但从字节流的角度来看,接收方收到的字节流必须和发送方发出的字节流是完全一样的,接收方有能力识别收到的字节流,把它还原成有意义的应用层数据,交付上层。

    如图5-8 :

从图5-8可以看出,TCP和UDP在发送报文时所采用的方式完全不同,TCP是面向字节流的,TCP并不关心应用进程一次把多长的报文发送到TCP缓存中,而是根据对方给出的窗口和当前网络拥塞的程度决定一个报文段应包含多少个字节,而UDP是面向报文的,只负责将从应用进程收到的报文添加首部后交付给IP层,所以UDP发送的报文长度是应用进程给出了。TCP可以灵活地控制报文的长度,如果TCP缓存的数据块太长,可以划分短一些再传送。如果太短,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。

3.2 TCP的连接

TCP把连接作为最基本的抽象

前面提到,每一条TCP连接有两个端点。那么,TCP连接的端点是什么?不是主机,不是主机的IP地址,不是应用进程,也不是运输层的协议端口,而是套接字(socket)插口。根据RFC793的定义:端口号拼接到(concatenated with) IP地址即构成了套接字。因此,套接字的表示方法是在点分十分制的IP地址后面写上端口号,中间用冒号或逗号隔开。例如,若IP地址是192.3.4.5而端口号是80,那么得到的套接字就是(192.3.4.5:80)。有:

socket=(IP)套接字socket=(IP地址:端口号)

每一条TCP连接唯一地被通信两端的两个端点(即两个套接字)所确定。即:

TCP::={socket1,socket2}={(IP1,port1),(IP2,port2)}TCP连接 ::= \{socket_1,socket_2\}=\{(IP_1,port_1),(IP_2,port_2)\}

IP1IP_{1},IP2IP_{2}分别为通信两个主机的IP地址,而port1port_{1},port2port_{2}分别是两个端点主机中的端口号。

再强调一遍,TCP连接的端点是个很抽象的套接字(即IP地址:端口号)。同时,同一个IP地址可以有多个不同的TCP连接,同一个端口号也可以出现在多个不同的TCP连接中。

4 可靠传输的原理

我们知道,TCP提供的是可靠传输,那么采用什么样的适当措施才能使得两个运输层之间的通信变得可靠?

首先理想的传输条件有以下两个特点

  • 一 传输信道不出现差错
  • 二 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。

在这样的理想传输条件下,不需要采取任何措施就能够实现可靠传输。然而实际的网络都不具备一下的两个条件,所以需要使用一些可靠传输协议,当出现差错时让发送方重传出现差错的数据(实现特点一),同时在接收方来不及处理收到的数据时,及时告知发送方降低发送数据的速度(实现特点二)。实现了这两个特点,就实现了可靠传输。下面就是具体讲如何去达到这两个特点

4.1 停止等待协议

运输层传送的协议的数据单元叫做报文段,网络层传送的协议数据单元叫做IP数据报。但下面为了讨论方便,把传送的数据单元都称为分组,而不考虑数据是在哪一个层次上传送的。

停止等待就是每发完一个分组就停止发送,等待对方的确认收到确认后再发送下一个分组。

4.1.1 无差错情况

停止等待协议可以用5-9来说明。图5-9(a)是传输分组中没有出现差错的情况。A发送完分组M1M_{1},就暂停发送,等待B的确认。B收到了M1M_{1}就向A确认。A收到了B对M1M_{1}的确认后,就继续发送下一个分组M2M_{2},然后就暂停发送,收到B对M2M_{2}的确认后,再发送M3M_{3}

4.1.2 出现差错

图5-9(b)是分组在传输过程中出现差错的情况。B收到了M1M_{1},但是M1M_{1}检测出了差错,就丢弃M1M_{1},其他什么也不做(并不通知A收到有差错的分组),另一种情况是M1M_{1}在传输过程中丢失了,这时B什么也不知道。在这两种情况下,B都不会发任何消息

可靠传输是这样设计的:A只要超过了一段时间没有收到B的确认,就认为刚才发送的分组丢失了(有可能被接收到了,但出错了,A一律看成是丢失了),因此A重传前面发送的分组,这就是超时重传。如何确定A要等待多长的时间才重传?我们在A每发送完一个分组时,设置一个超时计时器,如果在超时计时器到期之前收到了对方的确认,就撤销已经设置的超时计时器,如果超时计时器到期之前没有收到对方的确认,就重传分组。

这里需要注意的一些点是:

  • ,A在发送完一个分组后,必须暂时保留已发送的分组的副本,因为分组可能会丢失,需要重传,只有收到相应的分组确认后才可以清除分组的副本。
  • ,分组和确认分组都必须进行编号,这样才能明确是哪一个发送出去的分组收到了确认,而哪一个分组还没有收到确认。
  • ,超时计时器设置的重传时间应当比数据在分组传输的平均往返时间更长一些,以防止正常确认报文还未到达A,A就开始重传了,造成了不必要的重传。

4.1.3 确认丢失和确认迟到

图5-10(a)说明的是另一种情况,A发送的M1M_{1}分组顺利到达了B,且没有差错,B按协议向A发送一个M1M_{1}收到确认分组,但是该确认分组在传输的过程中丢失了,此时A并不知道M1M_{1}是丢失了,还是出错了,还是B的确认报文丢失了,因此A在超时计时器到底后就重传M1M_{1},假设传输顺利,B此时又收到了一遍M1M_{1},这是B会采取两个行动:

  • 丢弃这个重复的分组M1M_{1},不向上交付。
  • 向A重新发送确认,因为A之所以重传就表示A没有收到对M1M_{1}的确认,所以需要再确认一遍。

图5-10(b)也是一种可能出现的情况。传输过程中没有出现差错,但B对分组M1M_{1}的确认迟到了。A会收到重复的确认。对重复的确认的处理很简单:收下了重复的确认分组就丢弃。在这过程中,B也会收到重复的M1M_{1},但B会丢弃重复的M1M_{1},但每次收到M1M_{1}重传确认分组

这种可靠传输,A最终总是可以收到对所有发出的分组的确认,这样我们就可以在不可靠的传输网络上实现可靠的通信

像上述的这种可靠传输协议常称为自动重传请求ARQ(Automatic Repeat reQuest)。意思是重传的请求是自动进行的,接收方不需要请求发送方重传某个出错的分组。(靠的是发送方自己设置的超时计时器)。

4.1.4 信道利用率

停止等待协议的优点就是它的实现简单,但缺点是信道的利用率太低了。如图5-11,假定在A和B之间有一条直通的信道来传送分组。

假定A发送分组需要的时间是TDT_{D}TDT_{D}等于分组长度除以数据传输速率。再假定分组正确到达B后,B处理分组的时间忽略不计,即时立即发回确认。假定B发送确认分组需要时间TAT_{A},如果A处理确认分组的时间也忽略不计,那么A在经过时间**(TDT_{D}+RTT+TAT_{A})后才发送下一个分组。这里的RTT往返时间**。因为仅仅有TDT_{D}的时间用来传送有用的数据,因此信道利用率U为:

U=TD/(TD+RTT+TA)U=T_{D}/(T_{D}+RTT+T_{A})

为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。如图5-12,流水线传输就是发送方可以连续发送多个分组,不必每发完一个分组就停顿下来等待对方的确认,这样可以使得信道上一直有数据不间断地在传送。当使用流水线传输时,就需要使用连续ARQ协议滑动窗口协议

4.2 连续ARQ协议

图5-13(a)表示发送方维持的发送窗口,它的意思是:位于发送窗口内的5个分组都可以连续发送出去,而不需要等待对方的确认,通过这种方式,信道利用率就提高了。

如图5-13,分组发送是按照分组序号从小到大发送的。连续的ARQ协议规定,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。如图5-13(b)表示发送方收到了对第1个分组的确认,于是就把发送窗口向前移动一个分组。

接收方一般是采用累积确认的方式,也就是说,接收方不必对收到的分组逐个发送确认,而是在收到几个分组后,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已正确收到了。

累积确认的优点是:即使确认丢失了也不必重传,信道利用率比停止等待协议要高。但缺点是不能向发送方反映出接收方已经正确收到的所有分组的信息。

举一个累积确认的例子,如果发送方发送了前5个分组,而中间的第3个分组丢失,这时接收方只能对前两个分组发出确认,而发送方没有收到后面三个分组的确认,只要重传后面三个分组。这也叫做Go-back-N(回退N)。

5 TCP报文段的首部格式

我们知道,TCP是面向字节流的,但TCP传送的数据单元是报文段。一个TCP报文段分为首部数据两部分。

TCP报文段首部的前20个字节是固定的,后面有4n字节是根据需要而增加的选项(n为整数)。因此,TCP首部的最小长度是20字节

首部固定部分各字段的意义如下:

  1. 源端口目的端口:一个端口16位,各占两个字节,分别写入源端口号和目的端口号。实现前面提到的分用功能。

  2. 序号 占4个字节,序号范围是[0,23212^{32}-1],共2322^{32}(即4294967296)个序号。序号增加23212^{32}-1后,下一个序号就又回到。也就是说,序号使用mod 2322^{32}运算。
    我们说过,TCP是面向字节流的。在一个TCP连接中传送的字节流中的每一个字节按顺序编号。整个要传送的字节流的起始序号需要在连接建立时设置。首部中的序号字段就是指本报文段所发送的数据的第一个字节的序号。比如说,一报文段的序号字段值是301,而携带的数据共有100个字节(表面该数据报有100个字节流那么长),这就表明:本报文段的第一个字节的序号是301,最后一个字节的序号是400,所以下一个报文段的序号字段值为401。该字段也称报文段序号

  3. 确认号:占4个字节,是期望收到对方下一个报文段的第一个数据字节的序号(也就是报文的序号)。比方说B正确收到了A发送过来的一个报文段,序号字段为501,而数据长度为200字节,那么表明B正确收到了A发送的到序号700为止的数据。所以B期望收到A的下一个报文的序号是701,所以B在发送给A的确认报文段中把确认号设置为701
    总之,我们需要明白:

    若确认号 = N,就表明:到序号N-1为止的所有数据都已正确收到

  4. 数据偏移:占4位,指出TCP报文段的数据起始处距TCP报文段的起始处有多远,实际上也就是指出TCP报文的首部长度。因为首部中还有选项字段,所以得通过该字段确定数据部分在哪个位置开始。
    注意,数据偏移的单位是32个字(4字节),4位表示最大十进制数为15,所以数据偏移的最大值是60个字节,也就是TCP首部长度最大是60字节,也就是选项字段最大为40字节。

  5. 保留 6位,保留为今后使用,但目前全置为0。

    下面为**6个控制位 **(6)~(11)

  6. 紧急URG(URGent): 当URG=1时,表明紧急指针字段有效,告诉系统此报文段中有紧急数据,需要尽快传送,相当于说要优先传送这些数据,不要按原来的排队顺序来传送,这时发送方TCP就会将紧急数据插入到本报文段数据的最前面

  7. 确认ACK(ACKnowledgment): 仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1

  8. 推送PSH(PuSH): 我们前面提到了TCP协议在发送端和接收端都有缓存,数据放入缓存,而什么时候发送和什么时候接收给上层要等合适的时机,但有时候两个应用进程进行交互式的通信,在一端的应用进程希望在键入一个命令后就立即能够收到对方的响应,而不是还需要停在缓存在等待合适的时机。在这种情况下,TCP就可以使用推送字段,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地交付给上层应用进程,而不再等到整个缓存都填满了再向上交付

  9. 复位RST(ReSeT) 当RST=1时,表明TCP连接中出现了严重错误,必须释放连接,再重新建立运输连接。RST置1还用来拒绝一个非法的报文段或拒绝打开连接。

  10. 同步SYN(SYNchronization):在连接建立时用来同步序号。当SYN=1时而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段使SYN=1和ACK=1。因此,SYN置为1就表示这是一个连接请求连接接受报文

  11. 终止FIN(FINis)::用来释放一个连接。当FIN=1时,表示该报文段的发送方的数据已发送完毕,并要求释放运输连接。

  12. 窗口 占2个字节。窗口值为[02161][0,2^{16}-1]之间的一个整数。窗口指的是发送本报文段的一方的接收窗口。窗口值告诉对方:从本报文段首部的确认号算起,接收方目前运行对方发送的数据量(以字节为单位),之所以这么做是因为接收方接收缓存是有限的,如果接收的数据超过了缓存,会出现分组丢失。

    举个例子,A发送了一个报文段,确认号为701,窗口字段是1000。意思就是告诉对方:“从701号算起,我的接受缓存还能接受1000个字节的数据”。

    窗口字段明确指出了现在允许对方发送的数据量,经常需要根据实际缓存可接收空间而变化。

  13. 检验和:占2字节,检验的范围和UDP一样,包括首部和数据这两部分。检验方法与UDP检验和类似,只是一些字段需要改变。如伪首部中的第四个字段需要改成6,第五个字段UDP长度应改为TCP长度。

  14. 紧急指针:2个字节,仅当URG=1时才有意义,指出了本报文段中紧急数据的字节数,指出了紧急数据的末尾在报文段中的位置。值得注意的是,窗口为0也可以发送紧急数据

  15. 选项 长度可变,前面提到,最长可达40字节

6 TCP可靠传输的实现

为了方便理解,假定数据传输只在一个方向进行,即A发送数据,B给出确认。

6.1 以字节为单位的滑动窗口

TCP的滑动窗口是以字节为单位的。假定A收到了B发来的确认报文段,告诉窗口是20字节,而确认号是31,也就是说B期望收到的下一个报文的序号字段为31,并且序号30之前的报文已经收到了。根据信息,A构造出字节的发送窗口,如图5-15:

发送窗口表示:在没有收到B的确认的情况下,A可以连续把窗口内的数据都发送出去,需要再强调一遍:凡是已经发送出去的数据,但还没有收到确认,这些数据都必须暂时保留,以便在超时重传时使用

发送窗口里面的序号表示允许发送的序号,在介绍TCP报文字段时提到,接收方会把自己的接收窗口数值放在窗口字段中发送给对方。因此,A的发送窗口一定不能超过B的接收窗口的值,否则会出现接收方处理不过来而导致某些报文段的丢失。

发送窗口的位置由窗口前沿后沿的位置共同确定。发送窗口后沿的变化情况有两种:不动就表示没有收到新的确认,前移就表示收到了新的确认了。发送窗口前沿一般是不断向前移动的,但也有可能不动的,可能是因为没有收到新的确认而对方的接收窗口大小也保持不变,也可能是收到新的确认报文了,但是窗口大小变小了。

如图5-16,要描述一个发送窗口的状态需要三个指针,P1P2P3P_{1},P_{2},P_{3}指针都指向字节的序号,三个指针指向的部分的意义如下:

小于P1P_{1}的是已发送并已收到确认的部分,而大于P3P_{3}的是不允许发送的部分。

P3P_{3}-P1P_{1}=A的发送窗口。

P2P_{2}-P1P_{1}=已发送未收到确认的字节数。

P3P_{3}-P1P_{1}=允许发送尚未发送的字节数。

再观察B的接受窗口,B的接收窗口的大小是20。如图,到30号为止的数据是已经发送过确认,并且已经交付主机了。所以B不再保留这些数据,接收窗口内的序号31~50是允许接收的。如图5-16,B收到了序号为32和33的数据,但并没有收到序号为31的数据,我们前面有提到,B只能对按序收到的数据中的最高序号给出确认,因此B发送的确认报文段中的确认号还是31

假定B之后收到了序号为31的数据,并把序号3133的数据交付主机,然后B就删除这些数据,把接收窗口向前移动3个序号,同时给A**发送确认**。窗口仍然为20,但**确认号变为34**。A收到B的确认后,将发送窗口向前滑动3个序号,指针$P_{2}$暂时不动,A现在可发送的序号范围是4253。

之后A继续发送序号42~53的数据,指针P2P_{2}向前移动和P3P_{3}重合,此时发送窗口内的序号都已经使用完了,但还没有收到确认,可用窗口为0,停止发送,等待接收方发出确认。

6.2 窗口与缓存的关系

前面提到,发送方和接收方都存在有缓存,发送方的应用进程把字节流写入TCP的发送缓存,接收方的应用进程从TCP的接收缓存中读取字节流。进一步讨论窗口缓存的关系。如图5-19:

发送缓存用来暂存:

  • 发送应用程序传送给发送方TCP准备发送的数据
  • TCP已经发送但尚未收到确认的数据

已被确认的数据会从发送缓存中删除。

接收缓存用来暂存:

  • 按序到达的,但尚未被应用程序接收读取的数据
  • 未按序到达的数据

接收应用程序来不及读取收到的数据,接收缓存就被逐渐被填满,直到接收窗口为0。如果接收应用程序能够非常及时快速地读取缓存中的数据,接收窗口就可以增大。

对于缓存和窗口,还要强调以下三点:

  • 在同一时刻,A的发送窗口并不是总是和B的接收窗口一样大。原因是:我们知道A的发送窗口是根据B发送的报文的窗口字段来确定的,而B发送的报文的窗口字段虽然是根据接收窗口的大小确定的,但是通过网络传送窗口报文组需要经历一段的时间滞后,所以导致A的发送窗口并不总是和B的接受窗口一样大。
  • TCP通常对不按序到达的数据先临时存放在接收窗口中,等到字节流中所缺少的字节收到时,再按序交付上层的应用程序
  • TCP要求接收方需要有累计确认的功能,这样可以减小传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上,称为捎带确认

6.3 超时重传时间的选择

前面提到,TCP的发送方在规定的时间内没有收到确认就要重传已发送的报文段,概念是很简单的,但是如何确认重传的时间选择却成为了TCP最复杂的问题之一。

如果把超时重传设计设置得太短,就会引起很多报文段的不必要重传设置得太长,又使网络的空闲时间增大,降低了传输效率

TCP采用了一种自适应的算法,它记录一个报文段发出的时间,以及受到相应的确认的时间。这两个时间之差就是报文段的往返时间RTT。TCP保留了RTT的一个加权平均往返时间RTTsRTT_{s},又称为平滑的往返时间,因为是对众多往返时间的加权平均,所以结果更加平滑

RTTsRTT_{s}具体的计算是:第一次测量RTT样本时,RTTsRTT_{s}就为所测量到的RTT的样本值,以后每测量到一个新的RTT样本,就按下面公示重新计算新的RTTsRTT_{s}:

RTTs=(1α)×(RTTs)+α×(RTT)新的RTT_{s}=(1-\alpha) × (旧的RTT_{s})+\alpha × (新的RTT样本)

α\alpha的取值由标准推荐。

前面我们说到,超时计时器设置的**超时重传时间RTO(RetransmissionTime-Out)**应略大于RTTsRTT_{s}。具体计算还需要看协议的标准。

6.4 选择确认SACK

这里还需要解决一个问题,就是收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据。那么能否只传送缺少的数据而不重传已经正确到达接收方的数据选择确认就是用来处理这种情况的可行方法。

举个例子说明**选择确认(Selective ACK)**的工作原理。如图5-21,可以看出,序号11000收到了,但序号10011500没有收到,1501~3000收到了,但是3001到3500又缺少了,再后面序号4501起都没有收到。

接收方收到了和前面的字节流不连续的两个字节块,如果这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,并把这些信息告诉发送方,使发送方不要再重复发送这些已经收到的数据了。

从图5-21可看出,和前后字节不连续的每一个字节快都有两个边界,左边界和右边界。在图中用四个指针标记这些边界。第一个字节块的左边界L1L_{1}=1501,但右边界R1R_{1}=3001而不是3000,这表明左边界指出字节块的第一个字节的序号,但右边界减1才是字节块中的最后一个序号。

RFC 2018规定:

  • 如果要使用选择确认,那么在建立 TCP 连接时,就要在 TCP 首部的选项中加上“允许 SACK”的选项,而双方必须都事先商定好。
  • 如果使用选择确认,那么原来首部中的“确认号字段”的用法仍然不变。只是以后在 TCP 报文段的首部中都增加了 SACK 选项,以便报告收到的不连续的字节块的边界。
  • 由于首部选项的长度最多只有 40 字节,而指明一个边界就要用掉 4 字节,因此在选项中最多只能指明4个字节块的边界信息。(只能是因为40个字节还需要有的字节用来指明允许SACK等)

大多数的实现还是重传所有未被确认的数据块

7 TCP的运输连接管理

我们知道,TCP是面向连接的协议,运输连接是用来传送TCP报文的。TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的。

因此,运输连接分为三个阶段:连接建立,数据传送,连接释放。运输连接的管理就是使运输连接的建立和释放都能正常地进行。

连接建立过程中要解决以下三个问题

  • 要使每一方能够确知对方的存在。
  • 要允许双方协商一些参数(如最大报文段长度最大窗口大小服务质量等)。
  • 能够对运输实体资源(如缓存大小,连接表中的项目等)进行分配。

7.1 TCP的连接建立

TCP连接的建立都是采用客户服务器方式。主动发起连接建立的应用进程叫做客户(cilent),被动等待连接建立的应用进程叫做服务器(server)

TCP的建立连接的过程叫做握手。握手需要再客户和服务器之间交换三个TCP报文段,称为三次握手,如图5-28:

主机A运行TCP客户程序,B运行TCP服务器程序。最初两端的TCP进程均处于**CLOSED(关闭)**状态。A主动打开连接B被动打开连接

最开始,B的TCP服务器进程先创建传输控制块TCB,准备接受客户进程的连接请求,服务器进程处于**LISTEN(收听)**状态,等待客户的连接请求,如有,作出响应。

A的TCP客户进程也是首先创建传输控制模块TCB。然后,在打算建立TCP连接的时候,向B发出连接请求报文段,此时首部中的同步位SYN=1,表明这是一个连接请求报文,同时选择一个初始序号seq=x。注意,TCP规定,SYN=1的报文段不能携带数据,但是要消耗掉一个序号。发送完连接请求报文后,TCP客户进程就进入了(SYN-SENT),也就是同步已发送状态。

B收到连接请求报文段后,如果同意建立连接,则向A发送确认, B 在确认报文段中应使 SYN=1, ACK=1,表明这是一个连接接受报文,其确认号ack = x+1(表明下一个想接收到的报文的序号为x+1,自己选择的序号 seq =y。这是TCP服务器进程进入了SYN-RCVD,也就是同步收到状态。

TCP客户进程收到B的确认后,还要向B再给出确认。其确认报文的ACK=1,确认号ack=y+1,seq=x+1。同时,A的TCP通知上层应用进程,告诉连接已经建立了,可以发数据了。注意,TCP的标准规定ACK报文段可以携带数据,但如果不携带数据则不消耗序号,这种情况下一个数据报文的序号仍然是seq=x+1。发送完报文后,A进入ESTABLISHED,也就是已建立连接状态。B收到A的确认后,也进入已建立连接状态,通知上层TCP连接已经确立。

7.2 为什么连接建立,A最后还要发送一次确认?

主要是为了防止已失效的连接请求报文段突然传送到了B,产生错误。假定A发出的第一个连接请求报文段在某些网络结点长时间滞留了,A等待超时计时器时间走完没有收到确认,于是再次发起连接请求,请求成功,A和B正常同时并完成了连接的释放。过了一段时间,第一次滞留在网络中的连接请求报文到达了B,这本来是一个早已失效的报文段了,但B收到此失效的连接请求报文段后,以为是A又发出了一次新的连接请求,于是B会正常地向A发送确认报文。如果我们的协议是B只要发出确认,新的连接就建立了,那么A这时候就会很莫名其妙,我没有发出连接请求啊,因此A不会理睬B的确认,也不会向B发送数据,但B却误以为新的运输连接已经建立,一直处于等待状态,B因此浪费了许多的资源。

7.3 TCP的连接释放

数据传输后,通信双方都可以释放连接。连接释放前,A和B都处于已建立连接(ESTABLISHED)状态,如图5-29。

A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的终止控制位FIN=1,其序号为seq=u。这时A进入FIN-WAIT-1 终止等待1状态,等待B的确认。

B收到连接释放报文段后立即发出确认号,确认号是ack=u+1,而整个报文段自己的序号seq=v,然后B就进入了CLOSE-WAIT 关闭等待状态。TCP服务器进程这时通知高层应用进程。从A到B这个方向的连接就释放了,TCP连接处于半关闭状态,也就是A已经没有数据要发送了,但B若发送数据,A仍要接收

A收到来自B的确认后,就进入了 FIN-WAIT-2 终止等待2状态,等待B发出的连接释放报文段。

若B已经没有要向A发送的数据了,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段同样使FIN=1,ACK=1,seq等于对应的序号w,ack=u+1(与上一个确认报文相同,因为A早已经不再发送数据了)。发送完后,B就进入了LAST-ACK 最后确认状态,等待A的确认。

A收到B的连接释放报文段后,必须发出确认,在确认报文段中把ACK置1,确认号ack=w+1,而自己的序号是seq=u+1,之后进入到TIME-WAIT 时间等待状态。

现在的TCP连接还没有释放掉,必须经过时间等待计时器(TIME-WAIT timer)设置的时间2MSL,A才进入到CLOSED状态。时间MSL叫最长报文段寿命(Maximum Segment Lifetime)。

为什么A在TIME-WAIT状态必须等待2MSL的时间呢?有两个理由:

  • 第一,为了保证A发送的最后一个ACK报文段能够到达B。因为这个ACK报文段可能会丢失,因而B会超时重传连接断开报文字段,A就能在2MSL时间内收到这个报文段,重传一次确认。
  • 第二,防止前面提到的已失效的连接请求报文段出现在本连接中,A在发送完最后一个ACK报文段后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段,都从网络中消失,保证下一个新的连接中不会出现这种旧的连接请求报文段。

8 TCP的流量控制

8.1 利用滑动窗口实现流量控制

一般来说,我们总是希望数据传输得更快一些,但如果发送方把数据发送得过快,接收方就可能来不及接收,造成了数据的丢失。所谓**流量控制(flow control)**就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发送拥塞。利用滑动窗口机制可以很方便地在TCP连接上实现流量控制。

如图5-22流量控制举例:

A向B发送数据,在连接建立时,B告诉A:“我的接收窗口rwnd=400(字节,不是报文段)”,因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值

假设每一个报文段为100字节长,数据报文段序号的初始值设为1。

从图中注意到,接收方的主机B进行了三次流量控制。第一次把窗口减小到rwnd=300,第二次减到rwnd=100,最后减到rwmd=0,即不允许发送方再发送数据了。这种暂停的状态将持续到主机B重新发出一个新的窗口值为止。

现在我们需要考虑一种情况:在图5-22中,B向A发送了零窗口的报文段不久后,B的接收缓存有了一些存储空间,于是B向A发送了rwnd=400的报文段,告诉A“现在有空间了,你可以继续发送数据了”,然而非常非常不幸运的是,这个报文段居然丢失了,A就一直等一直等等收到B发送的非零窗口的通知,而B心想:“我都告诉你有空间了,咋还不发数据”,也在一直等一直等。如果没有其他措施,这种互相等待的死锁局面将一直延续下去。

为了解决这个问题,TCP为每一个连接设有一个持续计时器(persistence timer)。只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口探测报文段,而对方就在确认这个探测报文段时给出现在的接收窗口值。如果接收窗口为0,那么收到这个报文段的一方就重新设置持续计时器,如果不是0,就打破了死锁的僵局。

8.2 TCP 的传输效率

前面提到了,应用进程把数据传送到TCP的发送缓存后,剩下的发送任务就由TCP来控制了,可以用不同的机制来控制TCP报文段的发送时机

  • 第一种机制是 TCP 维持一个变量,它等于最大报文段长度MSS。只要缓存中存放的数据达到MSS字节时,就组装成一个TCP报文段发送出去。

  • 第二种机制是由发送方的应用进程指明要求发送报文段,即TCP支持的推送(push)操作。

  • 第三种机制是发送方的一个计时器期限到了,这时就把当前已有的缓存数据装入报文段(但长度不能超过MSS)发送出去。

交互式程序中的效率

发送方发送1字节的数据,同时要求服务器端回显这1字节数据。服务器确认后,再发回这1字节数据。则效率:1B/(41B的报文+40B的确认(20IP首部+20TCP首部+无数据)+41B的回显报文+40B的确认)
办法:

  • 使用捎带确认: 适当推迟发回确认报文,并尽量使用捎带确认的方法
  • Nagle算法:若发送方的应用程序把数据逐个送到TCP发送缓存,发送方将第一个数据字节发送出去,收到确认后,再把缓存中的数据数据组装成一个报文段发送出去。原则是:只有收到对前一个报文段的确认,才继续发送下一个报文段

9 TCP的拥塞控制

9.1 拥塞控制的一般原理

在计算机网络中的带宽,交换结点中的缓存等都是网络的资源。在某段时间,若对网络中某资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏–产生拥塞(congestion)

  • 出现资源拥塞的条件:对于资源需求的总和 > 可用资源
  • 如果网络中许多资源同时产生拥塞,网络的性能就要明显变坏,整个网络的吞吐量将随输入负荷的增大而下降。

9.1.1这里辨别一下拥塞控制流量控制的关系:

拥塞控制与流量控制的关系密切,但也存在差别。拥塞控制防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制是一个全局性的过程,涉及到网络中所有的主机,所有的路由器以及与降低网络传输性能有关的所有因素。

流量控制往往是指点对点通信量的控制,是个端到端的问题,流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收

举个例子,设某个光纤网络的链路传输速率为1000Gbit/s。一台巨型计算机向一台个人一点以1Gbit/s的速率传送文件。(端到端)显然,网络本身的带宽资源是足够的,不存在拥塞的问题,但必须进行流量控制,因为个人电脑不如巨型计算机快。

而如果有另一个网络,链路传输速率为1Mbit/s,而有1000台大型计算机连接在网络上。假定其中的500台计算机分别向其余的500台计算机以100kbit/s的速率发送文件,很明显网络的资源已经不够了,整个网络的输入负载已经超过了网络所能承受的。

9.1.2 理想拥塞控制和实际拥塞控制对比

如图5-23:

横坐标是提供的负载(offered load),代表单位时间内输入给网络的分组数目,也称为输入负载网络负载。纵坐标是吞吐量(throughput),代表单位时间内从网络输出的分组数目。

具有理想拥塞控制的网络,在吞吐量饱和之间,网络吞吐量应等于提供的负载,所以吞吐量曲线是4545^{。}的斜线,但当提供的负载超过某一个限度时,网络资源受限,吞吐量不再增长而保持为水平线,提供的负载有一部分将会丢失了。虽然如此,但是在这种理想的拥塞控制作用下,网络的吞吐量仍然维持在其所能达到的最大值。

但是,实际网络的情况就不一样了,随着提供的负载增大,网络的吞吐量的增长速率在逐渐减小,也就是说,在网络吞吐量还未达到饱和时,就有一部分输入负载被丢弃了。并当网络的吞吐量明显地小于理想的吞吐量时,网络就进入了轻度拥塞的状态。当提供的负载达到某一数值,网络的吞吐量反而下降了,这时网络就进入了拥塞状态。当提供的负载继续增大,网络的吞吐量将下降到0,无法工作,进入了所谓的死锁状态

9.1.3 开环控制和闭环控制

  • 开环控制方法就是在设计网络时事先将有关发生拥塞的因素考虑周到,力求网络在工作时不产生拥塞
  • 闭环控制是基于反馈环路的概念。属于闭环控制的有以下几种措施:
    1. 监测网络系统以便检测到拥塞在何时、何处发生。
    2. 将拥塞发生的信息传送到可采取行动的地方。
    3. 调整网络系统的运行以解决出现的问题。

9.2 TCP的拥塞控制方法

TCP进行拥塞控制的算法有四种,即慢开始(slow-start)拥塞避免(congestion avoidance)快重传(fast retransmit)快恢复(fast recovery)

首先介绍一些概念:

发送方维持一个叫做**拥塞窗口cwnd(congestion window)**的状态变量。拥塞窗口的大小取决于网络的拥塞程序,并动态地变化。发送方让自己的发送窗口等于拥塞窗口

接收端窗口rwnd: 这是接收端根据其目前的接收缓存大小所许诺的最新的窗口值,是来自接收端的流量控制。接收端将此窗口值放在 TCP 报文的首部中的窗口字段,传送给发送端。

发送方控制拥塞窗口的原则是:只要网络不出现拥塞,拥塞窗口就可以再增大一倍,以转发更多的分组。但只要出现拥塞或有可能出现拥塞,就把拥塞窗口减小一些,以缓解拥塞。发送方如何知道网络发生了拥塞?判断网络拥塞的依据就是出现了超时(其实并不准确,后面会解释)。

发送窗口的上限值:发送端的发送窗口的上限值应当取为接收端窗口 rwnd和拥塞窗口cwnd 这两个变量中较小的一个,即应按以下公式确定:

=Min[rwnd,cwnd]发送窗口的上限值=Min[rwnd,cwnd]

当 rwnd < cwnd 时,是接收端的接收能力限制发送窗口的最大值。当 cwnd < rwnd 时,则是网络的拥塞限制发送窗口的最大值。

9.2.1 慢开始算法原理

慢开始算法的思路:由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增加拥塞窗口数值,原因很简单,因为刚开始我们并不知道网络的负荷情况,所以先发较少的数据探测一下,具体规定为:

  • 在主机刚刚开始发送报文段时可先将拥塞窗口cwnd设置为一个最大报文段 MSS 的数值。
  • 在每收到一个对新的报文段的确认后,将拥塞窗口增加至多一个 MSS 的数值。
  • 用这样的方法逐步增大发送端的拥塞窗口 cwnd,可以使分组注入到网络的速率更加合理。

下面举例说明慢开始算法的原理: 如图5-24

报文段的个数作为窗口大小的单位

当TCP连接进行初始化时,将拥塞窗口cwnd=1,发送第一个报文段M1M_{1},接收方收到后确认M1M_{1},发送方收到对M1M_{1}的确认后,把cwnd从1增大到2,于是发送方接着发送M2M_{2}M3M_{3}两个报文段,接收方都收到后发回对M2M_{2}M3M_{3}的确认。发送方每收到一个对新报文段的确认,就使发送方的拥塞窗口加1,因此发送方收到两个确认后,cwnd就从2增大到4了,可以发之后的四个报文段了。

因此使用慢开始算法后,每经过一个传播轮次(transmission round),拥塞窗口cwnd就加倍。 如图5-24,一个传播轮次所经历的时间其实就是往返时间RTT,这里还需要注意RTT不是恒定的数值,一个传播轮次的时间更加具体的说是:cwnd所允许发送的报文段都连续发送出去,并全部收到了确认经历的总时间

9.2.2 慢开始门限ssthresh

为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量。慢开始门限的用法是:

  • 当cwnd<ssthresh时,使用上述的慢开始算法
  • 当cwnd>ssthresh时,停止使用慢开始算法而改用拥塞避免算法
  • 当cwnd=ssthresh,两种算法都可以使用

9.2.3 拥塞避免算法原理

拥塞避免算法的思路是让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口+1,而不是像慢开始算法那样加倍增长,也叫加法增大(Additive Increase),cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。

如图5-25,在慢开始算法下,当拥塞窗口cwnd增长到慢开始门限值ssthresh时,就改为执行拥塞避免算法,拥塞窗口按线性规律增长,使得网络比较不容易出现拥塞,但并非不是完全能够避免了拥塞。

当拥塞窗口cwnd=24时,网络中出现了超时,如图中的点2,表明已经出现了网络拥塞,发送方于是调整门限ssthresh=cwnd/2=12,重新设置拥塞窗口cwnd=1,重新进入慢开始阶段,当拥塞窗口到达了新的门限12时,就继续改为执行拥塞避免算法。

我们说到网络中出现了超时,就表明已经出现了网络拥塞,这个只是大多数的情况下,这么说其实并不准确,有时个别报文段在网络中丢失了,但实际上并不是网络拥塞造成的,这会导致发送方错误地重新设置慢开始门限,把拥塞窗口cwnd设置为1,因而降低了传输效率。(因为网络并没有拥塞,还不需要去控制拥塞窗口。)

9.2.4 快重传

怎么去解决上面说 的这种误会呢?采用快重传算法可以使发送方尽早知道发生了个别报文段的丢失

快重传算法首先要求接收方不要等待自己发送数据时才捎带确认,而是立即发送确认。这样做可以让发送方及早知道有报文段没有到达接收方。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段。这样就不会出现超时,发送方也就不会误以为出现了网络拥塞。

9.2.5 快恢复

如图在5-25中的点4,发送方一连收到3个对同一个报文段的重复确认,知道现在只是丢失了个别的报文段。于是不启动慢开始,而是执行快恢复算法。这是,发送方调整门限值ssthresh=cwnd/2=8,同时设置拥塞窗口cwnd=ssthresh=8,开始执行拥塞避免算法。

10 总结(jiade)

终于终于写完了~~~,真是又臭又长,TCP的东西实在是太多了,还有好多细节没有写到。

总结一下

我们从为什么需要运输层说起,提到了真正在计算机网络中进行的通信是位于通信两端主机中的进程之间的通信,IP协议以下层协议只能把分组送到目的主机,而运输层就是来解决交付给哪个进程的问题,紧接着介绍了运输层的功能,包括复用分用,以及提供应用进程间的逻辑通信,运输层提供了两种信道,可靠信道,对应TCP协议,不可靠信道,对应UDP协议,之后介绍了端口----用于给应用层的每个应用进程赋予一个非常明确的标志。之后介绍了UDP协议,主要特点是无连接不可靠面向报文无拥塞控制

然后就开始了漫长的TCP协议学习…

TCP的主要特点是面向连接点到点的(连接的端点是套接字),可靠的全双工通信面向字节流

这是我们第一次遇到可靠交付的情况,所以我们介绍了可靠传输的原理,知道了理想的传输条件有以下两个特点

  • 一 传输信道不出现差错
  • 二 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。

实现了这两个特点,就实现了可靠传输,然后就开始具体学习如何实现这两个特点,学习可靠传输的原理,首先是停止等待协议,因为停止等待协议信道利用率太低,又引入了更高效率的ARQ连续协议

然后就在可靠传输原理上,学习TCP可靠传输的实现,介绍了滑动窗口超时重传时间,之后是TCP的运输连接,三次握手四次挥手,最后介绍了TCP的流量控制和拥塞控制。

真的真的结束了,感觉对运输层的理解还是形成不了体系,所以本篇博客只是自己对课程学习的一些笔记和总结,大部分内容参考自**《计算机网络》**第七版,谢希仁编著。

如有出错(非常大可能会出错哈哈),欢迎指正!

完…

应该不会有人看到这吧哈哈哈哈哈~~~~~~ (溜了溜了)

文章作者: luo
文章链接: https://luo41.top/2021/06/18/计算机网络运输层/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 luo's Blog