计算机网络基础 — 运输层常见问题与解答

2024-05-14 17:08

本文主要是介绍计算机网络基础 — 运输层常见问题与解答,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一  运输层常见问题

问题1:运输层在 TCP/IP 协议栈中的地位和作用?为什么运输层是必不可少的?运输层的通信和网络层的通信有什么重要的区别?

从通信和信息处理的角度看,运输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是用户功能中的最低层。当网络的边缘部分中的两个主机使用网络的核心部分的功能进行端到端的通信时,只有主机的协议栈才有运输层,而网络核心部分中的路由器在转发IP数据报(IP分组)时都只用到下三层(即网络层、数据链路层、物理层)的功能。

        从网络层来看,通信的两端是两个主机,IP数据报的首部明确地标识了这两个主机的IP地址。但“两个主机之间的通信”这种说法还不够清楚。这是因为,真正进行通信的实体是在主机中的进程,是两个主机中的进程在交换数据(即通信)。因此严格地讲,两个主机进行通信就是两个主机中的应用进程在互相通信。IP协议虽然能把分组送到目的主机,但是这个分组还停留在主机的网络层而没有交付主机中的应用进程。

        从运输层的角度看,通信的真正端点并不是主机而是主机中的进程。也就是说,端到端的通信是应用进程之间的通信(如下图1所示)。因此,运输层是必不可少的。

图1  运输层为互相通信的应用进程提供了逻辑通信

        运输层的通信和网络层的通信有很大的区别。网络层提供主机之间的逻辑通信,而运输层则提供应用进程之间的逻辑通信。运输层还有复用、分用的功能,还要对收到的报文进行差错检测。

问题2:TCP协议是面向连接的,但TCP使用的 IP 协议是无连接的。这两种协议都有哪些主要的区别?

:这个问题很重要,一定要弄清楚。

        TCP协议是面向连接的,用于运输层,向上面的应用层提供通信服务;而IP协议是无连接的,用于网络层,向上面的运输层提供通信服务。二者的区别请见下表所示:

TCP 和 IP 向上提供的功能和服务区别
TCP提供的IP提供的
面向连接的服务无连接的服务
字节流接口IP数据报接口
有流量控制无流量控制
有拥塞控制无拥塞控制

保证可靠传输服务:

        无丢失

        无重复

        按序交付

不保证可靠传输服务:

        可能丢失

        可能重复

        可能失序

        从上表可以看出,TCP提供的功能和服务比IP所能提供的多得多。这是因为TCP使用了诸如确认、窗口通知、计时器等机制,因而可以检测出有差错的报文、重复的报文和失序的报文。

问题3:从通信的起点和终点来比较,TCP 和 IP 的不同点是什么?

:用下面的示意图就可以说明。

图2  可靠传输与不可靠传输的范围

         进程A和进程B的通信是使用面向连接的TCP提供的可靠的数据传输服务的。

        主机X和主机Y的通信是使用无连接的IP提供的不可靠的数据传输服务的。

        请注意:对TCP来说,通信的起点和终点是运输层上面的两个套接字(socket),而应用层的应用进程正是通过应用层和运输层之间的套接字来使用TCP提供的服务的。TCP协议根据TCP报文段首部中的端口号字段找到目的端口,将报文段交付给目的进程。请注意:套接字是有IP地址+端口号共同决定的,套接字也可称为“插口”。

        对IP来说,通信的起点和终点是连接在网络上的两个主机。IP协议根据IP数据报首部中的目的IP地址找到目的主机,将数据报交付给目的主机。

        请注意上图2中的可靠传输和不可靠传输的范围是不同的。

        我们还需要注意的是:虽然两个套接字之间的通信是面向连接的,但IP数据报在下面的网络中传输时是独立地选择路由的,而不是沿着某一条固定的路径传输。然而在上面的运输层看来,TCP报文段好像是从一个虚拟的、可靠的通信管道中传输到对方的运输层的。

问题4:IP 和 UDP 的一个共同特点就是它们都是无连接的。IP 和 UDP 最主要的区别是什么?

:IP 是主机到主机的通信协议;而 UDP 是进程到进程的通信协议。

问题5:套接字(socket)和端口(port)的区别是什么?

:在计算机网路编程中,套接字是操作系统的一种软件抽象,用于表示两个通信主机之间的连接端点(endpoint)。端口号拼接到(concatenated with)IP地址即构成了套接字(socket)或插口,而我们把IP地址和端口的组合定义为端点,因此我们也可以认为:“套接字就是通信端点的实例”。比如说,TCP套接字就是TCP连接的端点。

TCP连接的端点不是指通信的主机,也不是指通信的应用进程,也不是指运输层的协议端口,这一点也要清楚。

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

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

TCP连接 := {socket1, socket2} = {(IP1: port1), (IP2: port2)}

拓展》套接字(socket)这个名词有时容易使人把一些概念弄混淆,因为随着互联网的不断发展以及网络技术的进步,同一个名词socket却可以表示多种不同的意思。例如:

(1)当应用程序调用网络协议的API(Application Programming Interface,应用编程接口)函数时,即运输层和应用层之间的网络编程接口,称为socket API,并简称为socket。

(2)在 socket API 中有一个函数名也叫做socket。

(3)调用socket()函数的端点称为socket,如“创建一个数据报 socket”。

(4)调用socket()函数时,其返回值称为socket文件描述符,可简称为socket。注意,在Linux系统中,被创建的socket也是一种文件类型。

(5)在操作系统内核中,网络协议的代码实现,称为 socket 实现。

上面提到的5种socket概念与网络连接的socket(指端口号拼接到IP地址)的概念不同。需要加以注意一下。

        端口的本意是出口或入口的点,在计算机网络中的含义是应用层服务的一种代号,用来区分给定网络IP地址上的不同套接字。我们可能会有疑问:有了IP地址,为什么还需要端口号呢?这是因为IP地址只负责把数据传输到目的主机上,而一个主机它可能会有多个套接字,为了把数据传递给正确的套接字,就需要使用端口号来标识这个套接字。根据设置的端口号,操作系统负责把要发送或接收的数据分配给特定的套接字。在发送数据时,应用层的数据通过正确的套接字向下交付给运输层。在接收数据时,运输层的数据通过正确的套接字向上交付给应用层的某个应用程序。

        端口号是一个16位的整型数,可分配的端口号范围是0~65535。其中,0~1023 都是知名端口号(Well-known port),一般分配给特定应用程序,例如万维网服务器使用的端口号是80。

问题6:TCP都使用哪些计时器?

:TCP共使用四种计时器,分别是:重传计时器、持续计时器、保活计时器和时间等待计时器。这几个计时器的特点如下:

  • 重传计时器

        当TCP发送报文段时,就创建该特定报文段的重传计时器。可能发生两种情况:

(1)若在计时器截止时间到来之前收到了对此特定报文段的确认,则撤销此计时器。

(2)若在收到了对此特定报文段的确认之前计时器截止期到,则重传此报文段,并将计时器复位。

  • 持续计时器

        为了应对零窗口大小通知的情形,TCP需要一个持续计时器。假定“接收TCP”给出了一个窗口大小为零的确认,则“发送TCP”就停止发送报文段,直到“接收TCP”发送确认并给出一个非零的窗口大小。但这个确认报文段可能会丢失。我们知道在TCP中,对确认报文段是不发送确认的。若确认报文段丢失了,而“接收TCP”并不知道它已经丢失了,而是会认为它已经成功被对方接收了,并等待着“发送TCP”接着会发送更多的报文段。但“发送TCP”由于没有收到确认,就一直在等待对方发送确认来通知窗口的大小。于是,双方的TCP都在永远地等待着对方,这就造成了死锁。

        为了避免这种死锁情况的发生,TCP为每一个连接使用一个持续计时器。当“发送TCP”收到一个窗口大小为零的确认时,就启动持续计时器。当持续计时器期限到时,“发送TCP”就发送一个特殊的报文段,叫做探测报文段。这个报文段只有一个字节的数据。它有一个序号,但它的序号永远不需要确认;甚至在计算其他部分的数据的确认时,该序号也被忽略。探测报文段提醒“接收TCP”:确认已丢失,必须重传确认。

        持续计时器的值设置为重传时间的数值。但是,若发送端没有收到来自接收端的响应,则发送端需要继续发送另一个探测报文段,并将持续计时器的值加倍和复位。发送端继续发送探测报文段,将持续计时器设定的值继续加倍和复位,直到这个值增大到门限值(通常为60秒)为止。在这之后,发送端每隔60秒发送一个探测报文段,直到接收到接收端发来的一个非零窗口的报文段,即滑动窗口重新打开。

  • 保活计时器

        保活计时器使用在某些实现中,用来防止两个通信端点的TCP连接出现长时间的空闲。假定客户端打开了到服务器端的TCP连接,传送了一些数据,然后就保持静默了。也许这个客户端出现故障了。在这种情况下,这个连接将永远处于打开状态。

        要解决这个问题,在大多数的实现中都是使服务器设置保活计时器。每当服务器收到客户端的数据时,就将保活计时器复位。超时时间通常设置为2小时(7200秒)。若服务器端超过了2小时还没有收到客户端的信息,它就发送一个探测报文段。若发送了10个探测报文段(每一个间隔75秒)还没有响应,就假定客户端出了故障,这时服务器端就终止该连接。

  • 时间等待计时器

        时间等待计时器是在TCP连接终止期间使用的。当TCP关闭一个连接时,它并不认为这个连接马上就真正地关闭了。TCP传输协议它是全双工通信模式,当主动发起关闭连接的一方,收到对方的结束(FIN)报文段给出确认时,此时TCP连接还处于一种中间过渡状态,主动关闭方进入到TIME-WAIT 状态,请注意,此时TCP连接还没有释放掉。它会启动时间等待计时器,等待 2MSL 的时间后,才能进入到 CLOSED 状态,才算真正结束了此次TCP连接。如果在 2MSL 时间内,又重新收到了对端发来的 FIN+ACK 报文段,说明对端没有收到最后一个ACK确认报文段,对端于是超时重传了这个 FIN+ACK 报文段。接着,本端重传一次确认,重新启动 2MSL 计时器,等待 2MSL 的时间后,才能进入到 CLOSED 状态。

MSL(Maximum Segment Lifetime,最长报文段寿命)[RFC 793] 标准文档建议设置为 2 分钟。当然,TCP 允许不同的实现可以根据实际情况使用更小的 MSL 值。

问题7:是否 TCP 和 UDP 都需要计算往返时间 RTT?

:RTT(Round Trip Time,往返时间) 表示的是TCP报文段的往返时间。

        往返时间 RTT 只是对运输层的 TCP 协议才很重要,因为 TCP 要根据平均往返时间 RTT 的值来设置超时计时器的超时时间值。

        UDP 没有确认和重传机制,因此 RTT 对 UDP 没有什么意义。

        所以,不要笼统地说“往返时间RTT对运输层来说很重要”,因为只有TCP才需要计算RTT,而UDP不需要计算RTT。

问题8:假定 TCP 开始进行连接建立。当TCP发送第一个 SYN同步报文段时,显然无法利用教材中5.6.2节所介绍的方法计算得到新的往返时间RTT。那么这是TCP又怎样设置重传计时器呢?

:刚开始建立连接时,TCP显然无法利用已有的公式计算出往返时间RTT。实际上TCP是选择(也就是猜测)一个比较长的时间值作为初始的往返时间RTT。等到收到至少一个确认报文段时,才能利用公式计算出比较合理的往返时间RTT。

问题9:糊涂窗口综合征产生的条件是什么?是否只有在接收方才产生这种症状?

:糊涂窗口症合征产生的条件是:当发送应用程序产生数据慢,或接收应用程序读取数据(或消耗数据)很慢,或者两者都有。这时发送方和接收方都可能产生这种症状。

        不管是上述情况中的哪一种,都使得发送数据的TCP报文段都很小,这会导致TCP传输效率的降低。例如,若TCP发送的报文段只包含一个字节的数据,则意味着我们发送41字节的IP数据报(20字节的TCP首部+20字节的IP首部)才传送1字节的数据。数据的传送效率是 1/41,它表示我们在非常低效地使用网络的容量。

问题10:能否详细地讨论一下糊涂窗口综合征及其解决方法?

:详细讨论如下。

  • 发送端产生的症状

        如果发送端是产生数据很慢的应用程序,例如,一次只产生1个字节的数据。这个应用程序一次将一个字节的数据写入发送端的TCP发送缓存中。如果发送端的TCP没有特定的指令,它就产生只包含一个字节数据的TCP报文段。其结果是有很多41字节的IP数据报就在互联网中传来传去。

        解决的办法是防止发送端的TCP逐个字节地发送数据。必须强迫发送端的TCP先收集足够多的数据,然后用一个较大的数据块组装成一个报文段再发送出去。发送端的TCP要等待多长时间呢?如果等待时间过长,它就会使整个通信过程产生较长的时延。如果等待时间不够长,它就可能发送较小的报文段。Nagle(纳格)找到了一个很好的解决方法,它提出的这个方法称为 Nagle(纳格)算法。

  • Nagle算法

        Nagle 算法非常简单,但是它能解决发送端产生的糊涂窗口综合征的问题。这个算法是为发送端的TCP用的:

(1)发送端的TCP将它从发送应用程序收到的第一块数据发送出去,哪怕只有一个字节。

(2)在发送第一个报文段(即报文段1)以后,发送端的TCP就在发送缓存中积累数据并等待:或者接收端的TCP发送发送过来一个确认,或者数据已积累到可以组装成一个最大的报文段,在这个时候,发送端的TCP就可以发送这个报文段了。

(3)对剩下的传输,重复步骤2。这就是:如果收到了对报文段 x 的确认,或者数据已积累到可以组装成一个最大的报文段时,那么就发送下一个报文段(x+1)。

        Nagle算法的优点就是简单,并且它考虑到应用程序产生数据的速率,以及网络传输数据的速率。若应用程序比网络更快,则报文段就更大(最大报文段MSS)。若应用程序比网络慢,则报文段就较小(小于最大报文段MSS)。

  • 接收端产生的症状

        接收端的TCP可能产生糊涂窗口综合征,如果它是消耗数据很慢的应用程序,例如,一次消耗一个字节。假定发送应用程序产生了1000字节的数据块,但接收应用程序每次只读取1字节的数据。再假定接收端的TCP的接收缓存为4000字节。发送端先发送第一个4000字节的数据。接收端将其存储在其接收缓中,现在缓存满了,接收端的TCP就发送通知窗口大小为零的确认,这表示发送端必须停止发送数据。接收应用程序从接收端的TCP的接收缓存中读取第一个字节的数据。这是在接收缓存中就有了1字节的空间。接收端的TCP宣布其窗口大小为1字节,这表示正渴望等待发送数据的发送端的TCP会把这个宣布当作一个好消息,并发送只包含一个字节数据的报文段。这样的过程会一直持续下去。一个字节的数据被消耗掉,然后发送只包含一个字节数据的报文段。这又是一个低效率问题和糊涂窗口综合征。如下图所示:

图3  糊涂窗口综合征

         对于这种糊涂窗口综合征,即应用程序消息数据比到达的慢,有两种建议的解决方法。

  • Clark算法(克拉克算法)

        Clark算法的解决方法是只要数据到达就发送确认,但宣布的窗口大小为零,直到或者接收缓存空间已能放入具有最大长度的报文段,或者缓存空间的一半已经空了。

  • 延迟的确认

        第二个解决方法是延迟一段时间后再发送确认。这表示当一个报文段到达时并不立即发送确认。接收端在确认收到报文段之前一直等待,直到缓存有足够的空间为止。延迟的确认防止了发送端的TCP滑动其窗口(即发送端的滑动窗口不动)。当发送端的TCP发送完其数据后,它就停下来了。这样就防止了这种糊涂窗口综合征。

        延迟的确认还有另一个优点:它减少了通信量。接收端不需要确认每一个报文段。但它也有一个缺点,就是延迟的确认有可能迫使发送端重传其未被确认的报文段。可以用协议来平衡这个优点和缺点,例如,现在定义了确认的延迟不能超过500毫秒。

问题11:为什么TCP建立连接时不能每次都选择系统的、固定的初始序号?

:如果TCP在建立连接时每次都选择相同的、固定的初始序号,那么设想一下,假如可以的话,会遇到什么问题呢?

(1)假定主机A 和 B 频繁地建立连接,传送一些 TCP 报文段后,再释放连接,然后又不断地建立新的连接、传送报文段和释放连接。

(2)假定每一次建立连接时,主机A都选择相同的、固定的初始序号,例如,选择数字1作为初始序号。

(3)假定主机A发送出的某些TCP报文段在网络中会滞留很长的时间,以致造成主机A超时重传了这些TCP报文段。

(4)假定有一些在网络中滞留时间较长的TCP报文段最后终于到达了主机B,但这时传送该报文段的那个连接已经被释放了,而在到达主机B时的TCP连接时一条新的TCP连接。

        这样,工作在新的TCP连接下的主机B就有可能会接收到在旧的连接传送的、已经没有意义的、过时的TCP报文段(因为这个TCP报文段的序号有可能正好处在现在新的连接所使用的序号范围之中),结果产生错误。

        因此,必须使得迟到的TCP报文段的序号不会处在新的连接中所使用的序号范围之中。

        这样,TCP在建立新的连接时所选择的初始序号,一定要和前面的一些连接所使用过的序号不一样。所以,不同的TCP连接不能使用相同的初始序号。

问题12:假定在一个互联网中,所有的链路的传输都不出现差错,所有的网络结点也都不会发生故障。试问在这种情况下,TCP的“可靠交付”的功能是否就是多余的?

:不是多余的。TCP的“可靠交付”功能在互联网中起着至关重要的作用。

至少在以下所列举的情况下,TCP的“可靠交付”功能是必不可少的。

(1)每个IP数据报都是独立地选择路由,因此在到达目的主机时有可能出现失序。

(2)由于路由选择的计算出现错误,导致IP数据报在互联网中兜圈子。最后IP数据报首部中的生存时间TTL的数值下降到零。这个数据报在中途就被丢弃了。

(3)在某个路由器突然出现很大的通信量,以致路由器来不及处理已经到来的IP数据报。因此,有的IP数据报会被丢弃。

以上列举的问题表明了:必须依靠TCP的“可靠交付”功能才能保证在目的主机上运行的目的进程接收到正确的数据。

问题13:TCP是通信协议还是软件?

:协议与实现协议的软件的区别,类似于编程语言的定义与编译器之间的区别。与编程语言的情况类似,编程语言的定义与编程语言通过编译器在计算机上的实现之间的区别。通信协议它是定义协议内容的规范和标准,而软件是把某个协议用编程语言具体实现出来的程序。程序员们与TCP软件打交道的机会远远比TCP协议规范打交道的机会要多,因而会很自然地把某个协议的具体实现当作是协议的标准。尽管如此,我们必须明确地区分两者。

        总而言之,TCP是通信协议,而不是一个软件。

问题14:假定一个应用程序需要知道报文从发送端到接收端所经受的时延。能否这样计算:从TCP获取往返时间RTT的数值,然后除以2?

:不行。因为RTT仅仅是TCP内部的数据,上层的应用程序无法从TCP获得RTT的数值。但应用程序可以模仿TCP的做法,即从应用层发送一个报文给对方,等收到确认后,就能计算出报文的往返时间,把这个时间除以2,就能得出报文从发送端到接收端所经受的时延。

问题15:TCP的客户端和服务器端在交换数据时,为什么不含数据的确认报文段不消耗序号呢?

:因为给不含数据的确认报文段编号是没有必要的。具体分析如下:

        我们知道,TCP把要传送的数据中的每一个字节都编上序号,其目的就是为了保证每一个数据字节都能正确地传送到对方。接收方按字节的编号对收到的数据进行核对。用发送确认确认报文段的方法保证能够收到发送方发送过来的每一个数据字节。

        但是,一个有趣的问题来了。纯粹的确认报文段并不带有数据。可是这样确认报文段却仍有序号。之所以出现这个序号,是因为TCP首部中有这样一个序号字段。我们可以用下图的图示来说明这个问题。

图4  确认报文段不消耗序号

         在上图4中,我们假定TCP通信的一方A要发送三个TCP报文段,总共发送300字节数据给B,每个报文段携带100字节的数据。A 的报文段#1的序号是1。B收到后发送报文段#2。我们假定 B 现在的序号是5。A接着发送报文段#3。B发送确认报文段#4,其序号仍然是5。A在发送报文段#5。B 发送确认报文段#6,其序号仍然是5。我们假定 B 现在要用报文段#7 发送20个字节的数据给A,这个报文段的序号还是5。

        为什么 B 发送的四个报文段的序号都是5呢?因为 B 发送的前三个确认报文段都不消耗序号。

        但如果 B 在确认报文段中还携带有数据字节,那么这种确认报文段当然要消耗序号了。所消耗的序号数就是这个报文段携带的数据的字节数。

        我们可以看出,即使 B 发送的前三个确认报文段都丢失了,有 B 的第四个报文段(即报文段#7)的 ack = 301 就够了,A 就知道自己所发送的数据,在序号 300 以前的(包括序号为300的),B 已经都收到了。可见给不含数据的确认报文段编号是没有必要的。

问题16:TCP在建立连接时所发送的第一个SYN报文段(也叫同步报文段)只有首部,其数据部分是空的。但为什么 SYN 报文段要消耗一个序号?

:TCP 在建立连接时所发送的第一个SYN报文段是一个控制报文段,其主要目的是为了和对方建立同步,并告诉对方自己的初始序号。这个报文段没有数据部分,按理说,好像这个SYN报文段不需要消耗序号。但是 SYN 报文段非常重要,是不允许丢失的(传错了或丢失了就要重传,否则无法建立连接),这就必须进行编号。虽然 SYN报文段 没有数据部分,只有首部,但我们可以想象SYN报文段包含有一个虚字节的数据,因此给SYN报文段一个序号,让SYN报文段消耗要个序号。当对方收到序号为 x 的SYN报文段后,给出的确认就应当是 ack = x + 1。发送方收到这个确认,也就知道发送的SYN报文段已正确地传送给对方了。

问题17:TCP在建立连接时,A发送SYN报文段,选择了初始序号 seq=x。B收到连接请求报文段后,如同意建立连接,则向A发送确认和SYN报文段,其确认号是 ack=x+1,同时也为自己选择一个初始序号 seq=y。A最后要发送确认报文段,其序号是 seq = x+1,确认号是 ack=y+1。这个确认报文段消耗序号吗?

:这是TCP建立连接三报文握手过程。如下图所示:

图5  用三个报文段建立TCP连接

         A 发送的确认报文段#3 是不消耗序号的。如果 A 在确认报文段#3 之后接着发送数据报文段,那么这个数据报文段的序号仍然是 seq = x + 1,因为确认报文段#3 是不消耗序号的。

        但是应当注意的是,如果A 所发送的确认报文段#3 携带了数据,那么这个报文段就要消耗序号了。例如,A 所发送的确认报文段#3 携带了 100字节的数据,那么 A 下一次发送的数据报文段#4 的序号应当是 seq = x + 101。

《说明》在TCP建立连接的三报文握手过程中,具体实现TCP协议的软件一般不会在第三个确认报文段中携带数据的,所以第三个确认报文段时不消耗序号的。

问题18:TCP 连接建立采用的 Three-Way Handshaking 的准确译名应当是怎样的?

:过去很多人把 Three-Way Handshaking 译为“三次握手”,但这是不准确的。

        要建立TCP连接,客户端和服务器端的报文段交互的确需要三次,但这三次合起来应当算是一次握手过程。也就是说,要建立TCP连接,需要客户和服务器双方进行一次握手,但这种“一次”握手需要双方交换三个报文段(A到B,B到A,A再到B),形象地说就是:双方握一次手并抖三抖。

        在 RFC 793 文档中指出了,这种 Three-Way Handshaking 也就是“三报文握手”。因此,Three-Way Handshaking 最好是按意思译为“三报文握手”。

问题19:TCP报文段的长度有没有规定的最小值和最大值?

:我们首先要明确,在谈到“TCP报文段的长度”时,可能会有两种不同的理解。

第一种理解是:这是指TCP报文段的总长度(首部和数据部分的长度之和)。

第二种理解是:这是指TCP报文段数据部分的长度(不包括首部的长度)。

        这两种理解都是合理的。从字面上看,很自然会看到第一种理解是合理的。但我们知道,在TCP首部中的选项字段中有一个最大报文段长度MSS(Maximum Segment Size)。这个 MSS 指的是TCP报文段的数据部分的最大长度,并不包括首部长度。由于 MSS 是 TCP协议正式使用的名词,因此第二种理解也是合理的。

        这两种不同的理解有可能产生一些模糊不清的概念。为此,每当我们遇到 “TCP报文段长度”时,就应当弄清楚这里是指 “TCP报文段的总长度” 还是 “TCP报文段的数据部分长度”。

        由于这两种长度仅仅相差一个TCP报文段的首部长度,因此在讨论TCP报文段长度的最小值和最大值时,可以随便采用上面的一种理解,只要说清楚是哪一种长度即可。

        下面在讨论这个问题时,我们会采用很明确的表述方法,使读者不至于感到模糊不清。

        TCP报文段的总长度显然有一个最小值,这就是当TCP的数据部分的长度为零时的情况。例如,TCP确认报文段,仅仅有一个固定长度(20字节)的首部,其数据部分的长度为零。

        TCP报文段的总长度的最大值受以下两个方面的限制:

(1)IP数据报的总长度有一个规定的最大长度,即 65535 字节。如果使用20字节的固定长度的IP首部,那么TCP报文段的总长度一定不能超过 65515 字节。如果超过了这个数值,必须在运输层分割报文段,使每一个TCP报文段不超过 65515 字节,否则无法封装成 IP数据报。

(2)TCP报文段数据部分的长度不能超过最大报文段长度 MSS 值。我们知道,在TCP连接建立阶段,每一方都要向对方说明自己所能接收的数据长度是多少(因为收到的数据是要先存放在TCP的接收缓存中,如果数据太长就会放不下)。有人说,“在TCP连接建立阶段,通信双方要协商MSS的数值”。当这种说法是错误的(在 RFC 879 中专门指出这种说法是错误的,因为这里不存在什么协商)。通信的双方只是把自己的接收能力通知对方的TCP而已,并没有协商的过程。双方根据自己当时的具体条件给出的 MSS 值完全可以是不一样的。因此,TCP报文段数据部分的长度肯定不能超过对方TCP给出的 MSS 数值。

        这样,我们可以得出下面的表达式:

TCP报文段的总长度 ≤ Min[(对方给出的MSS + TCP首部), 65515字节]这里假定了IP数据报使用20字节的固定IP首部长度

问题20:能否这样讲:当我们在互联网上传送很长的大文件时,就必须使用TCP协议而不是使用UDP协议?

:上面的表述是不全面的。

        当我们通过互联网传送很长的大文件时(例如,作为电子邮件附件的大文件,或从某个网站/云盘下载一个大文件),一般都是使用 TCP协议,为的是保证在数据传送过程中不出现差错。我们都知道,一个大文件,只要在传送过程中间有一点小差错,就会导致整个大文件无法打开,从而导致整个文件传送失败。因此,这种大文件的传送,几乎都不使用UDP来传送,因为UDP不能保证不出差错。接收端收到有差错的 UDP 报文段时(校验和不正确),就简单的丢弃它。

        但也有例外的情况。

        假设我们正在计算机上观看一个实时的直播视频。这通常也是个很长的文件,被分割成很多文件块并实时播送。这些文件块一个接一个地被发送出去。如果在运输层认为应当重传损坏或丢弃的视频帧,那么整个传输的同步性就会丢失。观看者会突然看到一个空屏,然后需要等到第二次的传输到达。这是无法让人忍受的。但是,如果每一小块屏幕内容都用一个 UDP数据报 来传送,那么接收方就能简单地忽略损坏或丢失的分组,并将其他分组交付应用程序。部分屏幕可能会出现短暂的空白,但这对大多数观看者来说甚至都不会注意到。这就说明,在一些特殊情况下,不保证可靠传输的 UDP 协议也是很有用的。

二  习题

Q1:试举例说明有些应用程序原因采用不可靠的UDP,而不愿意采用可靠的TCP。

:这可能有以下几种情况。

        首先,在互联网上传输实时数据的分组时,有可能出现差错甚至丢失的情况。如果利用 TCP协议对这些差错或丢失的分组进行重传,那么时延就会大大增加。因此,实时数据的传输在运输层就应采用用户数据报协议UDP,而不是TCP协议。这就是说,对于传送实时数据,我们宁可丢弃少量分组(当然不能丢弃太多,否则重放的质量就太差了),也不要等待太晚到达的分组。在连续的音频或视频数据流中,很少量分组的丢失对播放效果的影响并不大(因为这是有人来主观评价的),因而是可以容忍的。在这种情况下,我们愿意采用不可靠的UDP,而不愿意采用可靠的TCP。

        其次,当网络出现拥塞时,TCP的拥塞控制就会让TCP的发送方放慢报文段的发送数据。可能有的应用程序就不愿意放慢其报文段的发送速度。另外,可以有的应用程序不需要TCP的可靠传输。在这些情况下,就宁可使用UDP来传送。

        常见的实时应用场景有:IP电话、实时视频会议、视频播放等。

Q2:当应用程序使用面向连接的TCP和无连接的IP时,这种传输是面向连接的还是无连接的?

:这要在不同层次来看。在运输层是面向连接的,而在网络层是面向无连接的。

Q3:试画图解释运输层的复用。画图说明许多个运输用户复用在一条运输连接上,而这条运输连接又复用到IP数据报上。

:下图给出了这种复用的简单例子。

图6  运输层复用的例子

         在上图6中,主机H3同时与主机H1和主机H2进行通信。H1和H3的两个应用进程(HTTP 和 SMTP)进行通信,这需要使用两个TCP连接。这两个TCP连接所传送的报文段(即这两个应用进程要传送的数据封装在同一个TCP报文段中),使用下面的网络层的IP数据报传送。H2 和 H3 的应用进程(HTTP)进行通信,这需要使用一个TCP连接。这个TCP连接所传送的报文段,也要使用下面的网络层的IP数据报来传送(即这两个TCP报文段封装在同一个IP数据报中)。在网络层所传送的IP数据报已看不到运输层以上的复用情况。

Q4:接收方收到有差错的UDP用户数据报时应如何处理?

:简单地丢弃处理。

Q5:如果应用程序愿意使用UDP完成可靠传输,这可能吗?请说明理由。

:这是可能的,但这要用应用层自己来完成可靠传输。例如,应用层自己使用可靠传输协议。当然,这还是需要相当大的工作量的。

Q6:为什么说UDP是面向报文的,TCP是面向字节流的?

  • UDP协议

        发送方的UDP对应用程序交下来的报文,在添加完UDP首部后就像下交付给IP层。UDP对应用程序交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文(如下图7所示)。

图8  UDP是面向报文的

         在接收方的UDP,对IP层交付上来的UDP用户数据报,在去除首部后就原封不动地交付上层的应用进程。也就是说,UDP一次交付一个完整的报文。因此应用程序必须选择合适大小的报文。若报文太长,UDP把它交付给IP层后,IP层在传送时可能要进行分片,这会降低IP层的效率。反之,若报文太短,UDP把它交付给IP层后,会使IP数据报的首部的相对长度太大,降低了IP层的传输效率。所以说,UDP是面向报文的。

  • TCP协议

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

图9  TCP面向字节流的概念

         TCP并不关心应用进程一次把多长的数据存储到TCP的缓存中,而是根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节,而UDP发送的报文长度是应用进程给出的,这是两者的区别。如果应用进程传送到TCP缓存的数据块太长,TCP就可以把它组装成多个TCP报文段再交付给IP层。如果应用进程一次只发来一个字节的数据,TCP也可以等待积累有足够多的字节后再组装成一个报文段发送出去。

Q7:端口的作用是什么?为什么端口号要划分为三种?

:端口是用来标志进程的。端口也就是协议端口号。但这种在协议栈层间的抽象的协议端口是软件端口,和路由器或交换机上的硬件端口是完全不同的概念。硬件端口是不同硬件设备进行交互的接口,而软件端口是应用层的各种协议进程与运输实体进行层间交互的一种地址。不同的系统,具体实现的方法是不同的(取决于主机使用的操作系统)。TCP/IP 的运输层用一个 16 位端口号来标志一个端口。但端口号只具有本地意义,它只是为了标识本计算机应用层中各个进程在和运输层交互时的层间接口。在互联网中不同的计算机中,相同的端口号是没有关联的。

        两台计算机中的应用进程要互相通信,不仅需要知道对方的IP地址(为了找到对方的计算机),而且还需要知道对方的端口号(为了找到对方计算机中的应用进程)。

        端口号有三种。不同的端口类别有其特殊的用途。例如,客户端是通信的发起方,而服务器是服务的提供方。它们对端口的使用要求是不同的。这三类端口号是:

(1)知名端口号,数值为 0~1023。这些数值可在 www.iana.org 插到。IANA 把这些端口号指派给了 TCP/IP 最重要的一些应用程序,让所有的用户都知道。

(2)登记端口号,数值为 1024~49151。这类端口号是为没有使用知名端口号的应用程序使用的。使用这类端口号必须按照 IANA 规定的手续登记,以防止重复。

上面两种端口号是服务器端使用的端口号。下面的一种是客户端使用的端口号。

(3)短暂端口号,数值为 49152~65535。这类端口号仅在客户端进行运行时才动态选择(由操作系统指派端口号数值),是留给客户进程选择暂时使用。

Q8:试说明运输层中伪首部的作用?

答:所谓 “伪首部” 是因为这种伪首部并不是 UDP用户数据报或 TCP 报文段真正的首部。只是在计算校验和时,临时添加在 UDP用户数据报 或 TCP报文段的前面,得到一个临时的 UDP用户数据报或 TCP报文段。校验和就是按照这个临时的UDP用户数据部或TCP报文段来计算的。伪首部既不向下传达也不向上递交,而仅仅是为了计算运输层的校验和。

Q9:某个应用进程使用运输层的用户数据报UDP,然后继续向下交给IP层,又封装成IP数据报。既然都是数据报,是否可以跳过UDP而直接交给IP层?哪些功能UDP提供了但IP没有提供?

答:IP数据报只能找到目的主机而无法找到目的进程。如果应用进程直接把数据交个下面的IP层,那么在传送到对方IP层后,就只能交付目的主机,但不知道应当将数据交付给哪一个应用进程。UDP提供对应用进程的复用和分用,以及提供对数据部分的差错检测。这些功能IP层没有提供。

Q10:一个UDP用户数据报字段为8192字节。在链路层要使用以太网来传送。试问应当划分为几个IP数据报片?说明每一个IP数据报片的数据字段的长度和片偏移字段的值。

:UDP用户数据报的长度 = 8192 + 8(UDP首部长度)=8200 B(字节)

        以太网数据字段最大的长度是 1500 B。若IP首部为20 B,则IP数据报的数据部分最多只能有 1500 - 20 = 1480 B。8200 = 1480 × 5 + 800,因此划分的IP数据报片共6个。

第1个数据报片的片偏移字节是 0。

第2个数据报片的片偏移字节是 1480 B。

第3个数据报片的片偏移字节是 1480 × 2 = 2960 B。

第4个数据报片的片偏移字节是 1480 × 3 = 4440 B。

第5个数据报片的片偏移字节是 1480 × 4 = 5920 B。

第6个数据报片的片偏移字节是 1480 × 5 = 7400 B。

下图给出了以上的结果:

图10  分片得到的6个IP数据报片及片偏移字节数

         把以上得出的片偏移字节数除以 8(片偏移是以8个字节为偏移单位) ,就得出片偏移字段中应当填入的数值。因此最后的答案,片偏移字段的值分别是:0,185,370,555,740,925。

Q11:使用TCP对实时话音数据的传输有没有什么问题?使用UDP传输数据文件时会有什么问题?

:对实时话音数据的传输是不能使用TCP的,这是因为用TCP传输话音数据时,只要TCP报文段一出现差错或丢失的情况,TCP就要重传。这就产生了额外的时延,有时这种时延会达到很高的数值,使接收方无法容忍。在实时话音通信中,我们宁可丢掉几个分组(这在重放时,还原的话音质量会差一些,但仍然可以听懂),也不愿意等待接收太迟到达的分组,因为这样会使重放话音质量严重恶化。虽然UDP不保证可靠交付,但UDP比TCP的开销小很多。因此只要应用程序可以接受这样的服务质量就可以使用UDP。

        如果话音数据不是实时播放(边接收边播放)就可以使用TCP,因为TCP传输可靠。接收端用TCP将话音数据接收完毕后,可以在以后的任何时间进行播放。但是在实时话音数据传输的场景下,必须使用UDP。

        使用UDP传送数据文件时,如果数据报出现了差错或丢失,UDP仅仅是少收了这个出错的报文段,并不通知发送方重传。这样就不能保证正确地传送数据。因此在传送数据文件时,我们都是采用TCP来传送的。

Q12:为什么在TCP首部中要把TCP的端口号放入最开始的4个字节?

:在ICMP(Internet Control Message Protocol,网际控制报文协议) 的差错报文中要包含 IP 首部后面的 8 个字节的内容,而这里面有TCP首部中的源端口和目的端口字段。当TCP收到 ICMP 差错报文时,需要用这两个端口来确定是哪条连接出了差错。

图11  ICMP差错报文的数据字段的内容

 Q13:为什么在TCP首部中有一个首部长度字段,而UDP的首部中就没有这个字段?

:TCP首部中用来表示首部长度的字段叫“数据偏移”字段,占4位,计算单位为:4字节长度(即32位),数据偏移字段的最大数值是15,因此TCP首部的最大长度是:15 × 4 = 60字节。

        TCP首部除了固定长度(20字节)部分外,还有选项,因此TCP首部长度是可变的,选项长度不能超过40字节。而UDP首部长度是固定的(8字节),不需要这个字段。

Q14:一个TCP报文段的数据部分最多为多少个字节?为什么?如果用户要传送的数据的字节长度超过了TCP报文段中的序号字段可能编码的最大序号值,问还能否用TCP来传送?

:一个TCP报文段的数据部分最多为 65495 字节。数据部分加上TCP首部的 20 字节,再加上IP首部的 20 字节,正好是 IP 数据报的最大长度 65535 字节。当然,若IP首部包含了选项,则 IP首部长度超过了 20 字节,这时TCP报文段的数据部分的长度将小于 65495字节。

        如果用户要传送的数据的字节长度超过了TCP报文段中的序号字段可能编码的最大序号值,仍然可以用TCP来传送。编号用完后再重复使用。但因设法保证不出现编号的混乱,一般都是从0开始重新编号。

Q15:在使用TCP传送数据时,如果有一个确认报文段丢失了,也不一定会引起与该确认报文段对应的数据的重传。试说明理由。

:重传计时器还未到时,发送端就收到了接收端发来的对更高序号的确认。

Q16:什么是Karn算法?在TCP的重传机制中,若不采用Karn算法,而是在收到确认时都认为是对重传报文段的确认,那么由此得出的往返时间样本和重传时间都会偏小。试问:重传时间最后会减小到什么程度?

:Karn(卡恩)算法允许TCP能够区分开有效的和无效的往返时间(RTT)样本,从而改进了往返时间的估算。

        若不采用Karn算法,而是在收到确认时都认为是对重传报文段的确认,那么由此得出的往返时间样本和重传时间都会偏小。如下图所示:

图12  认为收到的确认报文段是对重传的报文段的确认

         TCP发送了报文段后,没有收到确认,于是超时重传报文段。但刚刚重传了这个报文段后,马上就收到了确认。显然,这个确认是对原来发送的报文段的确认。但是,根据题意,我们就认为这个确认是对重传的报文段的确认。这样得出的往返时间就会偏小。这样的往返时间最后最后甚至可以减小到很接近于零。

        因此,上述的做法是不可取的。

Q17:在TCP的拥塞控制中,什么是慢开始、拥塞避免、快重传和快恢复算法?这里每一种算法各起什么作用?“乘法减小” 和 “加法增大” 各自用在什么情况下?

慢开始算法的思路是这样的:当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么就有可能引起网络拥塞,因此在事先不清楚网络负荷的情况下,先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。通常在刚开始发送报文段时,先把拥塞窗口值设置为一个最大报文段MSS的数值。然后每收到一个对新的报文段的确认后,把拥塞窗口值增加至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口 cwnd,可以使分组注入到网络的速率更加合理。使用慢开始算法后,每进过一个传输轮次,拥塞窗口 cwnd 就增大一倍。

        为了防止拥塞窗口 cwnd 增长多大引起网络拥塞,还需要设置一个慢开始门限 ssthresh 状态变量。当 cwnd > ssthresh 时,停止使用慢开始算法,而改用拥塞避免算法。

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

        快重传算法首先要求接收方每收到一个失序的TCP报文段后,就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方),而不要等待自己发送数据时才进行捎带确认。

        快恢复算法,其过程有以下两个要点:

(1)当发送方连续收到三个重复确认时,就执行 “乘法减小” 算法,把慢开始门限 ssthresh 减半。这是为了预防网络发生拥塞。请注意,接下去不执行慢开始算法。

(2)由于发送方现在认为网络很可能没有发生拥塞,因此现在不执行慢开始算法,而是把拥塞窗口cwnd 值设置为慢开始门限ssthresh 减半后的数值(即 cwnd = ssthresh),然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。

        “乘法减小” 是指无论慢开始阶段还是拥塞避免阶段,只要出现超时(即很可能出现了网络拥塞),就把慢开始门限值ssthresh 减半,即设置为当前的拥塞窗口的一半(与此同时,执行慢开始算法)。当网络频繁出现拥塞时,ssthresh 值就下降得很快,以大大减少注入到网络中的分组数。

        “加法增大” 是指执行拥塞避免算法后,使拥塞窗口缓慢增大,以防止网络过早出现拥塞。

Q18:TCP在进行流量控制时,是以分组的丢失作为产生拥塞的标志。有没有不是因为拥塞而引起分组丢失的情况呢?如果,请举例说明?

:不是因为网络拥塞而引起分组丢失的情况是有的,举例如下:

(1)当IP数据报在传输过程中需要分片,但其中一个数据报片未能及时到达终点,而终点组装IP数据报已过时,因而只能丢弃该数据报。

(2)IP数据报已经到达终点,但终点的缓存没有足够的空间存放此数据报。

(3)数据报在转发过程中经过一个局域网的网桥,但网桥在转发该数据报的帧时没有足够的存储空间而只好丢弃。

Q19:在下图所示的TCP连接释放过程中,在 ESTABLISHED 状态下,主机B 能否先不发送 ack = u +1 的确认?(因为B在后面要发送的连接释放报文段中,仍有 ack = u+1 这一信息。)

图13  TCP连接释放的过程

 :如果B不再发送数据了,是可以把这两个报文段合并为一个的,即只发送 FIN+ACK 报文段。但如果 B 还有数据要发送,那就不行了,因为A 迟迟收不到确认,就会认为刚刚发送的 FIN 报文段丢失了,就超时重传这个 FIN 报文段,浪费网络资源。

《拓展问题》在TCP连接释放的过程中,主机B向主机A发送FIN 报文段中,为什么还必须重复上次已发送过的确认号 ack = u+1 呢?即为什么不是 FIN=1, seq=w,而是 FIN=1,ACK=1, seq=w, ack=u+1?

:这是为了让主机A在接下来要发出的确认报文段中知道自己首部的序号字段的值。

Q20:试以具体例子说明为什么一个TCP连接可以有多种方式释放。可以设两个互相通信的用户分别连接在网络的两个结点上。

:设主机A是客户端,主机B是服务器端。A 和 B 建立了TCP连接后,A 向 B 传送了一个文件,当A收到来自B的确认后,文件的传送任务就完成了。A就释放TCP连接,B也随之释放TCP连接。这就是最简单的一种连接释放方法。

        但也有另一种情况。A 发送了一个文件后,里面含有一些数据,需要B进行修改。B收到文件后就发送了确认。A收到确认报文段后,就可以释放TCP连接,因为A已经没有数据要向B发送了,可以向B发送 FIN 报文段,然后B给出确认。这时的TCP连接处于半关闭状态。A不能再发送数据了,但B可以发送数据。等B向A发送修改完后的数据,B再向A发送FIN报文段,A确认后,TCP连接就释放了。这就是另一种连接释放的方法。

Q21:请解释说明为什么在TCP连接建立时要使用三报文握手。说明如果不这样做可能会出现什么情况?

:这是因为客户端向服务器发出的连接请求 SYN同步报文段可能因为某种原因滞留在网络中的某处。当该SYN报文段超时后,客户端会重传SYN报文段,并与服务器端成功建立了TCP连接,交换了数据,最后也释放了TCP连接。

        但滞留在网络中某处的已过时的SYN报文段,现在突然到达了服务器端。如果不使用三报文握手,那么服务器就认为客户端现在又请求建立TCP连接,于是就分配资源,等待客户端传输数据。但客户端此时并没有想要建立TCP连接,也不会向服务器传送数据,而服务器就一直死死等待着客户端发送数据,这就白白浪费了服务器的资源。

        如果使用三报文握手,那么服务器端在收到客户端发送的过时的SYN报文段后,就向客户端发送SYN报文段,并选择自己的初始序号 seq=y,并对客户端的SYN报文段给出确认,其确认号为 ack=x+1。当客户端收到服务器发来的SYN报文段时,从确认号就可得知不应当理睬这个SYN报文段(因为客户端现在并没有发送 seq=x 的SYN报文段)。这时,客户端发送RST(复位)报文段。在这个报文段中,RST=1,ACK=1,其确认号 ack=y+1。我们注意到,虽然客户端拒绝了TCP连接的建立(发送了复位报文段),但对服务器发送的SYN报文段还是给出确认已经收到了。

        服务器端收到客户端的 RST报文段后,就知道不能建立TCP连接,也就不会继续等待客户端发送数据了。

图14  用三报文握手建立TCP连接

Q22:在TCP的连接建立过程的三报文握手过程中,为什么第三个报文段不需要对方的确认?这会不会出现问题?

:关于这个问题,不能简单地用 “是” 或 “否” 来回答。

        我们假定A是客户端,B是服务器端。A向B发起建立TCP连接请求的过程中,现在假定三报文握手过程中的第三个报文段(也就是A发送的第二个报文段,即对B的确认报文段)丢失了,而A此时是不知道的。这时,A以为对方B收到了这个报文段,以为TCP连接已经建立了,于是开始发送数据报文段给B。

        服务器端B由于没有收到三报文握手中的最后一个确认报文段(客户端A发送的确认报文段),因此B就不能进入 ESTABLISHED 状态(即“连接已建立”状态)。B的这种状态叫做“半打开连接”状态,即仅仅只是把TCP连接打开了一半,即 B —> A 这个方向上的连接是打开的,而 A —> B 这个方向上的连接则仍是关闭的。在这种状态下,B 虽然已经初始化了连接变量和缓存,但是不能接收数据。通常,B 在经过一段时间后(例如,一分钟),如果还没有收到来自A的确认报文段,就终止这种“半打开连接”状态,那么A就必须重新建立TCP连接。因此在这种情况下,第三个报文段(A发送的第二个报文段)的丢失,就导致了TCP连接无法建立。既然无法建立起TCP连接了,也就不需要对方的确认了。

        但是,假定A在这段时间内,紧接着就发送了数据。我们知道,TCP具有累计确认的功能。在A发送的数据报文段中,自己的序号没有改变,仍然是和丢失的确认报文段的序号一样(丢失的那个确认报文段不消耗序号),并且确认位 ACK=1,确认号也是B选择的初始序号加1。当B收到这个数据报文段后,从TCP报文段的首部就可以知道,A 已确认收到了 B 刚才发送的 SYN+ACK 报文段,于是A就进入到了 ESTABLISHED 状态(“连接已建立” 状态)。接着,就接收A发送的数据。在这种情况下,A 丢失的第二个报文段(即TCP连接建立过程中的第三个握手报文段)对TCP的连接建立不会产生影响。

        大家知道,客户端A在发送第二个报文段时,可以有两种选择:

(1)仅仅是发送确认而不携带数据,数据接着在后面发送。

(2)不仅是确认,而且携带上自己的数据。

        在第一种选择中,A 在下一个报文段发送自己的数据。但下一个报文段的首部中仍然包括了对B的 SYN+ACK报文段的确认,即和第二种选择发送的报文段一样。

        在第二种选择中,A 在发出确认报文段的同时捎带上了数据部分,这也是可以的。

        从这里可以看出,A 发出的第二个报文段的作用仅仅是 B的 SYN+ACK报文段进行确认的报文段,如果不发送这个报文段就有可能出现Q21中所描述的异常问题发生。

Q23:UDP 和 IP 的不可靠程度是否相同?请加以解释。

:UDP 和 IP 都是无连接的协议和不可靠传输的协议。UDP用户数据报 和 IP数据报 的首部都有校验和字段。但检验出有差错时,就把收到的 UDP数据报 或 IP数据报丢弃。这是它们的相同之处。

        但 UDP 和 IP 的差错检测是有些区别的。UDP用户数据报 的校验和是既检验UDP用户数据报的首部又检验整个的UDP用户数据报的数据部分,而IP数据报的检验和仅仅检验IP数据报的首部。UDP用户数据报的检验和还增加了伪首部,即还检验了下层的IP数据报的源IP地址和目的IP地址。

Q24:UDP用户数据报的最小长度是多少?用最小长度的UDP用户数据报构成的最短IP数据报的长度是多少?

:UDP用户数据报的最小长度是 8 字节,即仅有首部而没有数据。用最小长度的UDP用户数据报构成的最短的IP数据报的长度是 28 字节。此IP数据报具有20字节的固定首部,首部中没有可选字段。

Q25:TCP连接处于 ESTABLISHED 状态。以下的事件相继发生:

(1)收到一个 FIN 报文段。(2)应用程序发送 “关闭” 报文。

在每一个事件之后,连接的状态是什么?在每一个事件之后发生的动作是什么?

:(1)处于 ESTABLISHED 状态又能够收到一个 FIN 报文段的,只有TCP连接的服务器端而不会是客户端。当这个服务器收到 FIN 报文段时,服务器就向客户端发送 ACK报文段,并进入到 CLOSED-WAIT 状态。这是被动关闭。请注意,这时客户端不会再发送数据了,但服务器端如果还有数据要发送给客户端,那么还是可以继续发送的。

(2)应用程序发送“关闭”报文给服务器,表明没有数据要发送了。这时服务器就应当发送 FIN 报文段给客户端,然后转入到 LAST-ACK 状态,并等待来自客户端的最后的确认报文段。

Q26:TCP连接处于 SYN-RCVD 状态。以下的事件相继发生:

(1)应用程序发送“关闭”报文。(2)收到FIN报文段。

在每一个事件之后,连接的状态是什么?在每一个事件之后发生的动作是什么?

:(1)处于 SYN-RCVD 状态而又能够收到应用程序发送的“关闭”报文的,只有TCP的客户端而不会是服务器端。这是客户端就应当向服务器端发送 FIN 报文段,然后进入到 FIN-WAIT-1 状态。

(2)当客户端收到服务器端发来的 ACK 确认报文段后,就进入到 FIN-WAIT-2状态,此时TCP连接处于半关闭状态。

(3)当客户端收到服务器端发来的 FIN 报文段后,就向服务器端发送 ACK 确认报文段,进入到 TIME-WAIT 状态,并启动时间等待计时器,经过2MSL时间之后,即2个报文最大生存时间,才最终进入 CLOSED 状态。

Q27:TCP连接处于 FIN-WAIT-1状态。以下的事件相继发生:

(1)收到 ACK 报文段。(2)收到 FIN 报文段。(3)发生了超时

在每一个事件之后,连接的状态是什么?在每一个事件之后发生的动作是什么?

:(1)处于 FIN-WAIT-1 状态的只有是 TCP连接的客户端。当收到 ACK 报文段后,TCP客户端不再发送任何报文段,只是从 FIN-WAIT-1 状态进入到 FIN-WAIT-2状态。

(2)在收到 FIN 报文段后,TCP客户端发送ACK确认报文段,进进入到 TIME-WAIT 状态。

(3)当发生了超时,也就是在经过了 2MSL 时间后,TCP客户端进入到 CLOSED 状态。

Q28:TCP的流量控制和拥塞控制最主要的区别是什么?发送窗口的大小取决于流量控制还是拥塞控制?

:简答地说,流量控制是在某一条TCP连接中的接收端采用的措施,用来限制发送方发送数据的速率,以免在接收端来不及接收。流量控制只控制一个发送端

        拥塞控制也是用来控制TCP连接中发送端发送数据的速率,以免使互联网中的某处产生过载。拥塞控制可能会同时控制许多个发送端,限制它们的数据发送速率。不过每一个发送端只知道自己应当怎样调整发送速率,而不知道在互联网中还有哪些主机被限制了发送速率。

        我们知道,发送窗口的上限值是 Min[rwnd, cwnd],即发送窗口的数值不能超过接收窗口和拥塞窗口中的较小的一个。接收窗口的大小体现了接收端对发送端施加的流量控制,而拥塞窗口的大小则是整个互联网的负载情况对发送端施加的拥塞控制。因此,当接收窗口小于拥塞窗口时,发送窗口的大小取决于流量控制,即取决于接收端的接收能力。但当拥塞窗口小于接收窗口时,则发送窗口的大小取决于拥塞控制,即取决于整个网络的拥塞状况。

Q29:TCP对拥塞控制采用的是动态调整的策略。能否给出动态调整的要点?

:TCP的拥塞控制的动态调整策略的要点有以下三点:

(1)探测网络的拥塞水平。慢开始阶段就是从发送一个报文段开始探测网络的拥塞状态的。

(2)如果网络没有拥塞,就加快发送速率。在慢开始和拥塞避免阶段都是这样。

(3)如果网络发生了拥塞,就降低发送速率。例如,回到慢开始,或让门限值 ssthresh 减半。

Q30:请用框图的表示方法来说明TCP的拥塞控制流程。

:下图给出了TCP的拥塞控制流程图。

图15  TCP的拥塞控制流程图

         整个流程图可分为三大阶段,即慢开始阶段、拥塞避免阶段和出现拥塞阶段。出现拥塞阶段在图中是最下方的虚线框,里面分为两种情况,一种是出现超时,另一种是收到了3个重复的ACK(确认)报文段。

        这里应当强调的是,当网络出现拥塞时,并没有一个什么机构来通知发送端使其降低发送速率。发送端探测网络是否发生了拥塞是根据能否及时收到对方发来的确认。如果出现了超时(就是在设定的时间内收不到对方发来的确认),这就说明发送了报文段的丢失。TCP认为,报文段的丢失就是网络发生拥塞的重要信号。因此,不管TCP处于哪一个阶段(慢开始阶段或拥塞避免阶段),只要出现超时,就要把慢开始阶段的门限值 ssthresh 减半,然后就进入慢开始阶段。拥塞窗口从 1 开始主见增大。

        但有时还没有到超时重传的时候,就收到了重复的确认ACK。收到了重复的ACK表明某个报文段没有到达接收端,但它后续的报文段却到达了。这就有两种可能性:一种是这个报文段是在传输途中的某个路由器中排队耽误了一些时间,过些时间就会到达接收端;而另一种可能性是这个报文段应丢失了,以后也不会到达接收端。TCP认为,如果只收到了一个或两个重复的ACK,那么这个报文段还没有丢失,还应当再等待一下。但是,如果一连收到了3个重复的ACK,那么就应当认为这个报文段大概是丢失了。于是立即重传这个报文段(快重传算法),并把门限值 ssthresh 减半,虽然发生了报文段的丢失,但并没有出现超时,而且接收方还能够连续收到3个重复的ACK。这表明虽然出现了网络拥塞,但拥塞的程度并不像出现超时那样严重。因此,TCP没有必要从一个报文段开始发送,而是可以在拥塞窗口减半的基础上把更多的报文段注入到网络中,即转入拥塞避免阶段(快恢复算法)。

参考

《计算机网络(第7版-谢希仁)》第5章 - 运输层

《计算机网络释疑与习题解答(第7版-谢希仁)》第5章 - 运输层

这篇关于计算机网络基础 — 运输层常见问题与解答的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/989335

相关文章

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou

java面试常见问题之Hibernate总结

1  Hibernate的检索方式 Ø  导航对象图检索(根据已经加载的对象,导航到其他对象。) Ø  OID检索(按照对象的OID来检索对象。) Ø  HQL检索(使用面向对象的HQL查询语言。) Ø  QBC检索(使用QBC(Qurey By Criteria)API来检索对象。 QBC/QBE离线/在线) Ø  本地SQL检索(使用本地数据库的SQL查询语句。) 包括Hibern

c++基础版

c++基础版 Windows环境搭建第一个C++程序c++程序运行原理注释常亮字面常亮符号常亮 变量数据类型整型实型常量类型确定char类型字符串布尔类型 控制台输入随机数产生枚举定义数组数组便利 指针基础野指针空指针指针运算动态内存分配 结构体结构体默认值结构体数组结构体指针结构体指针数组函数无返回值函数和void类型地址传递函数传递数组 引用函数引用传参返回指针的正确写法函数返回数组

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口int main(int argc, char *argv[]){// argc是命令行参数个数,argv是

【MRI基础】TR 和 TE 时间概念

重复时间 (TR) 磁共振成像 (MRI) 中的 TR(重复时间,repetition time)是施加于同一切片的连续脉冲序列之间的时间间隔。具体而言,TR 是施加一个 RF(射频)脉冲与施加下一个 RF 脉冲之间的持续时间。TR 以毫秒 (ms) 为单位,主要控制后续脉冲之前的纵向弛豫程度(T1 弛豫),使其成为显著影响 MRI 中的图像对比度和信号特性的重要参数。 回声时间 (TE)

Java基础回顾系列-第七天-高级编程之IO

Java基础回顾系列-第七天-高级编程之IO 文件操作字节流与字符流OutputStream字节输出流FileOutputStream InputStream字节输入流FileInputStream Writer字符输出流FileWriter Reader字符输入流字节流与字符流的区别转换流InputStreamReaderOutputStreamWriter 文件复制 字符编码内存操作流(