计算机网络——传输层

目录

1. 概述和运输层服务

1.1 运输层和网络层的关系

1.2  因特网运输层概述

2. 多路复用与多路分解

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

2.2  面向连接的多路复用与多路分解

3. 无连接运输:UDP

3.1 UDP所作的工作

3.2 使用UDP的例子

3.3 什么时候更适合UDP

3.4 UDP报文段结构

3.5 UDP校验和

4. 可靠数据传输的原理

4.1 构造可靠数据传输协议

        1.rdt1.0

        2.rdt2.0

        3.rdt2.1

        4.rdt2.2

        5.rdt3.0

4.2 流水线可靠数据传输协议

        1.回退N步(GBN)

        2.选择重传

5.面向连接的运输:TCP

5.1 TCP连接

5.2 TCP报文段结构

         1.序号和确认号

5.3 往返时间的估计与超时

        1.估计往返时间

5.4 TCP的可靠数据传输

5.5 流量控制

5.6 TCP连接管理

        1.连接步骤

        2.TCP拆除

        3.SYN洪范攻击

        4.如果服务器不在线怎么办

5.7 拥塞控制原理

        1.拥塞控制和流量控制比较

        2.拥塞的原因及代价

        3.拥塞控制方法

        4.TCP拥塞控制

        5.TCP性能分析


1. 概述和运输层服务

        运输层协议的作用:为运行在不同主机上的应用进程之间提供了逻辑通信

        逻辑通信的概念:从应用程序的角度看,通过逻辑通信,运行在不同进程的主机好像直接相连一样,实际上,这些主机可能位于地球的两端,通过很多路由器及多种不同类型的链路连接,应用进程使用运输层提供的逻辑通信功能彼此发送报文,而无须考虑承载这些报文的物理基础设施的细节。运输层协议是在端系统中而不是在路由器中实现的,网络路由器仅作用于该数据报的网络层字段,即它们不检查封装在该数据报的运输层报文段的字段。

1.1 运输层和网络层的关系

        运输层在网络层之上,网络层提供了不同主机之间的逻辑通信,而运输层提供了不同主机上的进程之间的逻辑通信。 

1.2  因特网运输层概述

        因特网为应用层提供的两种运输层协议。①UDP(用户数据报协议)协议②TCP(传输控制协议)协议。

        一些术语。RFC文档会将TCP的运输层分组称为报文段,将UDP的分组和网络层分组称为数据报,此处不采用;此处采用将TCP和UDP的分组统称为报文段,而将网络层的分组称为数据报

        简单介绍一下因特网的网络层。IP的服务模型是尽力而为交付服务,这意味着IP尽它“最大的努力”在通信的主机之间交付报文段,但它并不做任何保证。特别是它不确保报文段的交付,不保证报文段的按序交付,不保证报文段中数据的完整性。由于这些原因,IP被称为不可靠服务

        UDP和TCP的基本责任UDP和TCP最基本的责任是,将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务。将主机间交付扩展到进程间交付被称为运输层的多路复用与多路分解。UDP能提供的仅有的两种服务是进程到进程的数据交付(进程到进程之间的报文交付)和差错检查(检查并丢弃出错的报文),这同样是两种限度最低的运输层服务。与IP一样,UDP也是一种不可靠服务,即不能保证一个进程所发送的数据能够全部到达目的进程。TCP提供可靠服务,通过使用流量控制、序号、确认和定时器, TCP确保正确地、按序地将数据从发送进程交付给接收进程。这样,TCP就将两个端系统间的不可靠IP服务转换成了一种进程间的可靠数据传输服务,TCP还提供拥塞控制,力求为每个通过一条拥塞网络链路的连接平等地共享网络链路带宽,这可以通过调节TCP连接的发送端发送进网络的流量速率来做到。UDP流量不可调节,使用UDP传输的应用程序可以根据其需要以其愿意的任何速率发送数据。

2. 多路复用与多路分解

        运输层的多路复用与多路分解,也就是将由网络层提供的主机到主机交付服务延伸到为运行在主机上的应用程序提供进程到进程的交付服务如果某层的一个协议对应上层的多个协议/实体,则需要复用/分解

        多路复用:在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息(这将在以后用于分解)从而生成报文段,然后将报文段传递到网络层,所有这些工作称为多路复用。

        多路分解:将运输层报文段中的数据交付到正确的目的套接字的工作称为多路分解。

        具体工作流程: 多路复用和分解的根据是①套接字有唯一标识符;②每个报文段有特殊字段来指示该报文段所要交付到的套接字,例如运输层报文段中的源与目的端口号(端口号扩展:源/目的端口号是一个16比特的数,其大小在0~65535之间,0~1023范围的端口号是周知端口号,是受限制的,指它们保留给诸如HTTP(使用端口号80)之类的周知应用层协议来使用)。有了这两个根据,其工作流程如下在主机上的每个套接字能够分配一个端口号,当报文段到达主机时,运输层检査报文段中的目的端口号,并将其定向到相应的套接字。然后报文段中的数据通过套接字进入其所连接的进程。

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

        前提知识。UDP套接字是由(ip地址,端口号)这样一个二元组唯一标识的;运输层的报文段包含的内容有源ip地址、源端口号、目的ip地址、目的端口号和数据其它数据。

        工作流程。 假定在主机A中的一个进程具有UDP端口19157,它要发送一个应用程序数据块给位于主机B中的另一进程,该进程具有UDP端口 464280主机A中的运输层创建一个运输层报文段,其中包括应用程序数据、源端口号(19157)、目的端口号(46428)和两个其他值(将在后面讨论,它对当前的讨论并不重要)。然后,运输层将得到的报文段传递到网络层。网络层将该报文段封装到一个IP数据报中(网络层并不关心端口号),并尽力而为地将报文段交付给接收主机。如果该报文段到达接收主机B,接收主机运输层就检查该报文段中的目的端口号(46428)并将该报文段交付给端口号46428所标识的套接字。值得注意的是,主机B可能运行多个进程,每个进程都具有其自己的UDP套接字和相联系的端口号。当UDP报文段从网络到达时,主机B通过检查该报文段中的目的端口号,将每个报文段定向(分解)到相应的套接字。如果两个UDP报文段有不同的源IP地址或源端口号,但具有相同的目的IP地址和目的端口号,那么这两个报文段将通过相同的目的套接字被定向到相同的目的进程。运输层报文段中源端口号的用途是:在A到B的报文段中, 源端口号用作“返回地址"的一部分,即当B需要回发一个报文段给A时,B到A的报文段中的目的端口号便从A到B的报文段中的源端口号中取值。(完整的返回地址是A的IP地址和源端口号)

2.2  面向连接的多路复用与多路分解

        前提知识。TCP套接字是由(源ip地址,源端口号,目的ip地址,目的端口号)唯一标识的(与UDP区分开),也就是说TCP是通过不同主机的两个进程共同确定一个套接字(两个进程一对一),而UDP只需要自己的IP地址和端口号就可以确定一个套接字(目的进程可以接收不同源进程)。

        工作流程。当一个TCP报文段到达主机时,所有4个字段(源IP地址, 源端口,目的IP地址,目的端口)被用来将报文段定向(分解)到相应的套接字。

(未完待续,关于一个端口号是否可以被多个进程绑定的问题以及?)

解答:

Q:一个端口号是否可以被多个进程绑定?

A:如果进程先绑定一个端口号,然后在fork一个子进程,这样的话就可以是实现多个进程绑定一个端口号,但是两个不同的进程绑定同一个端口号是不可以的。一个端口号可以被一个进程的多个线程绑定。

Q:一个进程是否可以绑定多个端口号?

A:是的。

3. 无连接运输:UDP

3.1 UDP所作的工作

        UDP从应用进程得到数据,附加上用于多路复用/分解的源和目的端口号,以及长度和校验和字段,然后将形成的报文段交给网络层。网络层将该运输层报文段封装到一个IP数据报中,然后尽力而为地尝试将此报文段交付给接收主机。如果该报文段到达接收主机,UDP使用目的端口号将报文段中的数据交付给正确的应用进程。值得注意的是,使用UDP时,在发送报文段之前,发送方和接收方的运输层实体之间没有握手。正因为如此,UDP被称为是无连接的。

3.2 使用UDP的例子

        DNS是一个通常使用UDP的应用层协议的例子。当一台主机中的DNS应用程序想要进行一次查询时,它构造了一个DNS查询报文并将其交给UDP。无须执行任何与运行在目的端系统中的UDP实体之间的握手,主机端的UDP为此报文添加首部字段,然后将形成的报文段交给网络层。网络层将此UDP报文段封装进一个IP数据报中,然后将其发送给一个名字服务器。在查询主机中的DNS应用程序则等待对该查询的响应。如果它没有收到响应(可能是由于底层网络丢失了查询或响应),则要么试图向另一个名字服务器发送该査询,要么通知调用的应用程序它不能获得响应(很多时候我们访问某个网站时,第一次会提示没有接收到响应,然后刷新之后又可以继续访问)

3.3 什么时候更适合UDP

        ①UDP没有拥塞控制机制,而TCP有拥塞控制机制,TCP的拥塞控制机制以便当源和目的主机间的一条或多条链路变得极度拥塞时来遏制运输层TCP发送方。TCP仍将继续重新发送数据报文段直到目的主机收到此报文并加以确认,而不管可靠交付需要用多长时间。但是有一些实时应用通常有发送速率不小于某个值的要求,不希望过分地延迟报文段的传送以及能容忍一些数据的丢失,则UDP比TCP更适合这些应用。(注意,使用UDP的应用是可能实现可靠数据传输的,这可通过在应用程序自身中建立可靠性机制来完成,例如,可通过增加确认与重传机制来实现

        ②无需建立连接。TCP在开始数据传输之前要经过三次握手。 UDP却不需要任何准备即可进行数据传输。因此UDP不会引入建立连接的时延。 这可能是DNS运行在UDP之上而不是运行在TCP之上的主要原因(如果运行在 TCP上,则DNS会慢得多)。HTTP使用TCP而不是UDP,因为对于具有文本数据的Web网页来说,可靠性是至关重要的。

        ③无连接状态。TCP需要在端系统中维护连接状态。此连接状态包括接收和发送缓存、 拥塞控制参数以及序号与确认号的参数。我们将在3. 5节看到,要实现TCP的可靠 数据传输服务并提供拥塞控制,这些状态信息是必要的。另一方面,UDP不维护连接状态,也不跟踪这些参数。因此,某些专门用于某种特定应用的服务器当应用程序运行在UDP之上而不是运行在TCP上时,一般都能支持更多的活跃客户。

        ④分组首部开销小。每个TCP报文段都有20字节的首部开销,而UDP仅有8字节的开销(即源端口号、目的端口号、数据长度、检验和,每个字段占2个字节)。

3.4 UDP报文段结构

        首部有四个字段,每个字段各占2个字节。端口号可用于实现分用复用;长度字段指示了在UDP报文段中的字节数(首部+数据);接收方用校验和来检查该报文段中是否出现差错。

3.5 UDP校验和

        校验和完成的功能是差错检验。这就是说,检验和用于确定当UDP报文段从源到达目的地移动时,其中的比特是否发生了改变(例如,由于链路中的噪声干扰或者存储在路由器中时引入问题)。

        校验和工作原理。UDP计算校验和的方法和计算IP数据报首部校验和的方法相似。但不同的是:IP数据报的校验和只检验IP数据报的首部,但UDP的校验和是将首部和数据部分一起都检验。在发送端,首先是将全零放入检验和字段。再将伪首部以及UDP用户数据报看成是由许多16bit的字串接起来。发送方的UDP将报文段中的内容视为16比特为一个单位,含有若干个单位的数据,对这些若干个16比特数进行加和运算,遇到任何溢出都被回卷,即将溢出加到最低位上,得到的最终结果取反。举例子如下:

        假设我们有 3 个 16 比特的字,分别如下

0110 0110 0110 0000
0101 0101 0101 0101
1000 1111 0000 1100

         对这3个16比特的字依次相加,注意到如下:        接下来将0100 1010 1100 0010取反变成1011 0101 0011 1101放入校验和即可。在接收方中,将全部的4个16比特的字(包含了校验和)加在一起,没有差错的话,则为1111 1111 1111 1111。

        为什么在UDP上提供差错检验。①由于源和目的之间存在多条链路,但是不能保证所有的链路都提供差错检验;②假设报文段经过链路都能正确地传输,但当报文段存储在某台路由器的内存中时,也可能引入比特差错。注意,UDP只是提供差错检验,但是没有纠错能力,即只是丢弃受损的报文段或者将受损的报文段交给应用程序并给出警告。

4. 可靠数据传输的原理

        可靠数据传输协议的下层协议(如IP)也许是不可靠的,因此在不可靠的下层协议之上建立可靠的上层协议是困难的。下图是对传输是一个简单刻画:        应用层通过调用rdt_send(),可以将调用数据传输到传输层,rdt表示的是可靠数据传输,因为从应用层到传输层是运输层的本机传输,因此不会产生数据丢失。运输层将数据分成一个个分组,通过udt_send()调用下一层不可靠的信道进行传输,udt表示不可靠,从A传输到B时可能会产生差错,但是在B从网络层到传输层的本机传输是可靠的,因此为rdt_rcv()。接收方运输层将分组合并成数据再通过deliver_data()将数据传给应用层。

        本节使用有限状态机进行描述,引起状态改变的事件显示在横线上方,事件发生时的采取的动作在横线下边,如果对一个事件没有动作,则用A表示。

4.1 构造可靠数据传输协议

        本小节将逐步构造一个可靠数据传输协议。

        1.rdt1.0

        rdt1.0的假设:考虑最简单的情况,即运输层的底层信道是完全可靠的        由于底层信道是完全可靠的,则发送端的运输层通过应用层调用rdt_send获取数据后,再将数据通过make_pkt来产生一个包含该数据的分组,接着调用udt_send发送到底层信道,由于底层信道是完全可靠的,因此不会产生分组丢失或差错。接收端运输层通过rdt_rcv从完全可靠的信道接收分组,接着通过extract从分组中取出数据,最后通过deliver_data交付给应用层。

        2.rdt2.0

        rdt2.0的假设:底层信道并不完全可靠,在发送方到接收方传输过程中分组可能会出现比特受损的情况,但不会出现分组丢失或者乱序的情况。发送方在发送一个分组给接收方时,由于可能会出现比特受损的情况,因此发送方必须能够通过某种方法知道自己发送的分组接收方有没有能够正确的接收,这种方法就是,如果接收方接收到的是正确的分组,则就发送一个“肯定确认”(ACK)给发送方,告知发送方我已正确接收你发送的分组,否则发送一个“否定确认”(NAK)给发送方,告知发送方分组出现差错(此处假设ACK和NAK都可以正确的传输)。        整体流程如下:发送方调用udt_send发送分组后,会进入一个等待状态,必须等待确认接收方是否能够正确接收上一个分组,如果发送方接收到了来自接收方的分组并且是ACK,则进入等待上层调用的状态,否则重传上一个分组给接收方,继续等待。接收方收到分组后,如果分组出现比特错误(corrupt),则发送一个NAK给发送方,继续等待,否则则从分组中提取数据交给接收方的应用层并发送一个ACK给发送方。

        3.rdt2.1

        rdt2.1的假设是底层信道并不完全可靠,在发送方到接收方传输过程中和接收方发送ACK/NAK给发送方时分组或ACK/NAK可能会出现比特受损的情况,但不会出现分组丢失或者乱序的情况。

        为了应对ACK/NAK会出现比特错误的情况,有三种做法:①发送方对ACK/NAK接收进行反馈,但反馈也可能出现差错,因此会陷入循环的困局;②增加足够的检验和比特,使发送方不仅可以检测差错,还可以恢复差错,对于会产生差错但不丢失分组的信道,这可以解决问题;③如果发送方接收到模糊不清(即出现差错)的ACK或NAK分组时,发送方直接重发一份之前的分组给接收方即可。这样同时会存在一个问题,接收方可能会接收到重复的分组。

        为解决做法三的问题,我们可以在数据分组中添加一个新的字段,让发送方对其分组进行编号,即将发送数据分组的序号放入该字段,于是,接收方只需要检查序号即可确认收到的分组是否是一次重传。对于停等协议,只需0、1两个序号即可。        对于发送方,如果发出了一个序号为0的分组,就会进入到等待ACK0或NAK0的状态,如果此时接收到ACK/NAK是出现差错的,则会重新发一份序号为0的分组,只有接收到正确的且为ACK,才会进入下一个状态。        对于接收方,接收方必须接收到一个分组才会发出去一个ACK或NAK,发送方也必须接收到一个NAK或出现差错的ACK/NAK才会重传,因此发送方接收到的ACK或NAK必是对于发送方最近发送的分组的反馈,而不会是上上个或者之前的,所以ACK和NAK无需使用标记序号。接收方有两个状态,一个是等待接收0,一个是等待接收1,假设此时处于等待接收0的状态,如果收到了一个序号为1的分组(出现这种情况的原因只可能是接收方上一次发出去的ACK出现了差错),则重新发送一份ACK;如果收到了一个出现差错的分组,则发送NAK;如果收到比特正确且序号正确的分组,则从分组取出数据交给应用层并进入等待接收1的状态。

        4.rdt2.2

        rdt2.2的假设同rdt2.1。rdt2.1使用NAK和ACK来标记是否正确收到分组,rdt2.2则使用(ACK,1)和(ACK,0)来标记是否正确收到分组。        对于发送方,假设此时处在一个等待上层调用的0状态,如果发送调用,则将一个序号为0的分组发送给接收方,然后进入等待ACK0状态,如果接收方返回的是ACK1或者出现了比特差错,则发送方重新发一份序号为0的分组给接收方,直到收到正确的ACK0才会进入等待上层调用的1状态。        对于接收方,假设此时处于等待下层的0状态,如果收到了序号为1的分组(在这个状态中只有接收方发送的ACK出现比特差错发送方才会发一份序号为1的分组),则发送(ACK,1)告诉发送方我已经收到序号为1的分组了;如果收到了比特差错的分组,则发送(ACK,1)告诉发送方已经收到序号为1的分组了,若该出错的分组本来是发送方发送序号为0的分组,则发送方此时接收到(ACK,1)就知道刚刚发的分组没准确送到,则发送方会再发一次序号为0的分组。

        5.rdt3.0

        rdt3.0的假设:底层信道并不可靠,可能会出现比特受损和分组丢失的情况。为了解决分组丢失的情况,设置了一个倒计数定时器,如果发送方超过一定时间t未接收到ACK或NAK的话,则重传分组。注意到如果一个分组经历了一个特别大的时延,发送方可能会重传该分组,即使该数据分组及其ACK都没有丢失。这就在发送方到接收方的信道中引入了冗余数据分组的可能性。幸运的是,rdt2.2协议已经有足够的功能(即序号)来处理冗余分组情况。        假设发送方此时处于等待上层调用的0,如果接收到上层的调用,则会发送一个序号为0的分组给接收方,并启动倒计时,进入等待接收ACK0的状态,假设一接收方收到分组并且发了一个ACK0给发送方,但是丢失了,则倒计时结束时发送方没有接收到ACK0,就会重发一个序号为0的分组给接收方; 假设二接收方收到分组并且发了一个ACK0给发送方,没有丢失,但是时延过长导致倒计数结束接收方都未收到ACK0,此时发送方仍会重发一个序号为0的分组给接收方,如果之后接收到了第一个ACK0进入了等待上层调用的1之后再接收到第二个ACK0,那么就会什么都不做,如果之后接收到了第一个ACK0进入了等待上层调用的1之后再进入等待ACK1之后再接收到第二个ACK0,同样也会什么都不做(关于下面的情况1)。注意,处于等待接收ACK0状态的时候接收ACK1的情况只有三种,一种是情况1(对应上面),第二种是发送方发送的序号为0的分组出现了比特差错,第三种是接收方重复接受了序号为1的分组。发送方对于这三种情况采取的动作都是置之不理,唯一能让发送方重新发送分组的情况就是倒计时结束。

4.2 流水线可靠数据传输协议

        rdt3.0存在的问题。rdt3.0是一个可靠的协议,但是性能方面不尽人意,因为rdt3.0是一个停等协议,发送和接收一个完整的分组过程是发送方必须得发送分组、等待正确的反馈,这样之后才能进行下一个分组的发送。具体例子如下:

         流水线技术

        流水线协议对可靠数据传输同样也带来了一定的要求:①必须增加序列号,因为每个传输中的分组必须有一个唯一的序号,而且可能有多个在输送中的未确认报文;②协议的发送方和接收方需要缓存多个分组,发送方最低限度应该能缓存那些发出去但未确认的分组(因为可能没确认成功,需要重发),接收方或许也要缓存那些已正确接收的分组;③所需序号范围和对缓冲的要求取决于数据传输协议如何处理丢失、损坏以及延时过大的分组。

解决流水线的差错恢复有两种基本方法:

        1.回退N步(GBN)

        在GBN中,允许发送方发送多个分组(当有多个分组可用时)而不需要等待确认,但它也受限于在流水线中未确认的分组数不能超过某个最大允许数N。

        发送方可以看到一个如下的窗口。        基序号对应的是最早未确认分组的序号,下一个序号对应的是最小未使用序号(即下一个可用于发送分组的序号)。【我们为什么要限制这些发送的、未被确认的分组的数目为N呢,为什么不允许分组无限制呢?将在3.5节看到流量控制是对发送方施加限制的原因之一】

        对于发送方,①当接收到上层的rdt_send调用时,发送方首先判断当前是否有可用分组的序号,如果没有,发送方则将数据返回给上层,隐式地指示上层该窗口已满,然后上层可能过一会儿再试;如果还有可用分组的序号,则将该数据打包成附带序号nextseqnum的分组发出去,然后判断是否之前发送出去的分组都确认了(base==nextseqnum),如果都确认了,则新发出去的这一分组开启计时器(因为①是“多一个未确认分组”操作,如果之前的都被确认了,即当前除了刚发出去的分组以外是0个未确认分组,则刚发出去的分组要启动计时器),接着nextseqnum加1。②如果倒计时结束了,则重新启动倒计时并重发所有的未确认分组。③如果收到一个正确的确认信息,则将base置为接收到的确认分组的序号加1,再判断是否之前发出去的分组都确认了(base==nextseqnum),如果都确认了,则停止计时器(因为③是“少一个为确认分组”操作,因此此时若都没有未确认分组了,说明不需要计时了,因为都没未确认分组,记啥时),如果还有未确认分组,则重新启动倒计时。假如发出去了1、2、3、4、5分组,1、2、3、4都没接收到确认信息,但是接收到了序号为5的分组的确认接收信息,那么说明包含5之前的分组都被接收到了(因为接收方只有按序接收到了分组才会发确认信息)。④如果接收到的确认信息是有差错的,则置之不理。        对于接收方,①如果收到了没有差错且序号正确的分组,则将数据从分组提取出来上传给应用层,并且expectedseqnum(记录的是期待收到的下一个分组的序号)加1,假设此时接收方接收到了1、2、3、4序号的分组,则expectedseqnum=5,如果序号为5的分组还没到,而序号为6的分组先到了,那么接收方只是简单地将序号为6的分组进行丢弃(也可能缓存)然后发送最近一次接收到的分组的序号给发送方(对应②)。因此只要当发送方收到序号为5的确认信息(尽管可能没收到2、3、4),说明接收方已经正确接收了包含5以前的所有分组。

        2.选择重传

        选择重传在接收方和发送方都有滑动窗口,且二者窗口互不可见且非同步。选择重传要求接收方逐个地确认正确接收的分组,且对于乱序的分组也进行接收。每个分组都有一个定时器这块看视频吧,说不清楚。        选择重传相比于GBN的一个小区别就是,选择重传的接收方不会因为收到未接受过且乱序的分组而向发送方发送ACK,它要么将其忽略,要么将其放置窗口内,只有当接收到在窗口内的分组或窗口之前的分组就会向发送方发送ACK。而GBN的接收方是只要不是接收到合乎顺序的分组,都会向发送方发送一个ACK,用以指示它下一个期望收到的分组序号,所以GBN是累计确认。而且选择重传的每个每组都有一个计时器,超时的话只是重传超时分组,而不是全部重传超时分组及以后的分组。

5.面向连接的运输:TCP

5.1 TCP连接

        TCP连接是一条逻辑连接,其共同状态仅保留在两个通信端系统的TCP程序中,即TCP协议只在端系统中运行,而不在中间的网络元素(路由器和链路交换机)中运行,中间路由器对TCP连接完全看不见。

        TCP的三次握手简述。客户首先发送一个特殊的TCP报文段,服务器用另一个特殊的TCP报文段来响应,最后,客户再用第三个特殊报文段作为响应。前两个报文段不承载“有效载荷”,也就是不包含应用层数据;而第三个报文段可以承载有效载荷。

        建立TCP连接后发送数据的过程。客户进程通过套接字(该进程之门)传递数据流。数据一旦通过该门,它就由客户中运行的TCP控制了。TCP将这些数据引导到该连接的发送缓存里,发送缓存是发起三次握手期间设置的缓存之一。接下来TCP就会不时从发送缓存里取出一块数据,并将数据传递到网络层。TCP可从缓存中取出并放入报文的数据数量受限于最大报文长度MSS),MSS是由本地发送主机能够发送的最大链路层帧长度(即所谓的最大传输单元MTU)所设置的。设置该MSS要保证一个TCP的报文段(当封装在一个IP数据报中)加上TCP/IP首部长度(通常40字节)将适合单个链路层帧。注意,MSS是指在报文段里应用层数据的最大长度,而不是指包括首部的TCP报文段的最大长度

         TCP连接的组成。一台主机上的缓存、变量和与进程连接的套接字,以及另一台主机上的另一组缓存、变量和与进程连接的套接字。

5.2 TCP报文段结构

        TCP报文段=首部字段+数据字段。MSS限制的数据字段的最大长度。当TCP发送一个大文件,例如某Web页面上的一个图像时,TCP通常是将该文件划分成长度为MSS的若干块(最后一块除外,最后一块通常小于MSS)。注意,TCP和UDP的报文段头部都没有源IP地址和目的IP地址,那是交给网络层做的事        (注:前6行是首部字段)

  • 源端口号和目的端口号:用于分用和复用
  • 序号和确认号:用于可靠数据传输

         1.序号和确认号

        序号。TCP把数据看成无结构的、有序的字节流,然后对字节进行编号排序,而不是对报文段进行编号排序,一个报文段的序号就是该段第一个字节的序号。假设主机A上的一个进程想通过一条TCP连接向主机B上的一个进程发送一个数据流。主机A中的TCP将隐式地对数据流中的每一个字节编号。假定数据流由一个 包含500 000字节的文件组成,其MSS为1000字节,数据流的首字节编号是0。如图所示,该TCP将为该数据流构建500个报文段。给第一个报文段分配序号0, 第二个报文段分配序号1000, 第三个报文段分配序号2000, 以此类推。每一个序号被填入到相应TCP报文段首部的序号字段中。

        确认号。 主机A向主机B发送的确认号是主机A期望从主机B获取下一个字节的序号,并且表明在此序号之前的字节主机A都已经正确接收(TCP是累计确认)。举个例子,假设主机A已收到了来自主机B的编号为0 ~535的所有字节,同时假设它打算发送一个报文段给主机B。主机A等待主机B的数据流中字节536及之后的所有字节。所以主机A就会在它发往主机B的报文段的确认号字段中填上536。再举个例子,假设主机A已收到一个来自主机B的包含字节0 ~535的报文段,以及另一个包含字节900~1000的报文段。由于某种原因,主机A还没有收到字节536 ~899的报文段。在这个例子中,主机A为了重新构建主机B的数据流,仍在等待字节536 (和其后的字节)。因此,A到B的下一个报文段将在确认号字段中包含536而不是1001。此时存在一个问题,当主机在一条TCP连接中收到失序报文段时该怎么办(如例二中的先收到了900~1000而不是535~899)?TCP RFC并没有为此明确规定任何规则,而是把这一问题留给实现TCP的编程人员去处理。他们有两个基本的选择:①接收方立即丢弃失序报文段(如前所述,这可以简化接收方的设计);②接收方保留失序的字节,并等待缺少的字节以填补该间隔。显然,后一种选择对网络带宽而言更为有效,是实践中采用的方法。

5.3 往返时间的估计与超时

        TCP采用的超时/重传机制来处理报文丢失问题,那么如何设置这个定时器的时长是多少呢?首先可以肯定的是该时长必须大于该连接的RTT,否则会造成不必要的重传,但是随着网络的变化,RTT也会发送变化,因此就需要一个较为合理的估计。

        1.估计往返时间

        TCP会不定时地对某个报文段的RTT进行测量,称为sampleRTT,用以估计一般的RTT,但TCP不会为已重传的报文段进行测量sampleRTT,它仅为传输一次的报文段测量sampleRTT。为了估计一个典型的RTT,因此去sampleRTT的平均值,记为estimateRTT,一旦获得一个新的sampleRTT,TCP就会根据下列公式来更新estimateRTT。其中α的推荐值为0.125。定时器的时长=estimateRTT+安全边界,因此还存在一个问题,由于sampleRTT可能会存在波动,如果波动大的话则安全边界要大,反之则小。因此还需要一个值devRTT来估算波动程度,其公式如下:β的推荐值为0.25。最后定时器的时长公式如下:推荐TimeoutInterval的初始值为1s,出现超时后则该值加倍,以免即将被确认的后继报文段过早出现超时,同时,只要收到报文段并更新estimateRTT,就使用上述公式再次计算TimeoutInterval。

5.4 TCP的可靠数据传输

        前提。在我们前面研发可靠数据传输技术时,曾假定每一个已发送但未被确认的报文段都与一个定时器相关联,这在概念上是最简单的。虽然这在理论上很好,但定时器的管理却需要相当大的开销。因此,推荐的定时器管理过程仅使用单一的重传定时器,尽管有多个已发送但还未被确认的报文段。在本节中描述的TCP协议遵循了这种单一定时器的推荐。

        发送方。发送方主要做三件事。①从应用层获取数据,创建报文段,序列号是报文段第一个字节的序号,如果当时计时器没有开启,则开启计时器,向IP传递报文,修改记录当前序列号的值。②定时器超时,重传引起超时的报文段(仅一个,而不是像GBN那样的多个)。③收到ACK,如果ACK的字段值大于最小未确认分组的序列号,则重置最小未确认分组的序列号的值为ACK的字段值加1,如果当前还有未确认的分组,还需重启计时器。

        接收方。 接收方主要做4件事。①如果按序收到分组,并返回最后一个分组的ACK后,就会等待500ms,如果这500ms没有新的分组,则重新返回最后一个分组的ACK。②如果比期望序号大的失序报文先到达,则会发送之前发过的ACK,指示下一个期待字节的序号。(累计确认)

         快速重传。由于超时的话定时器时长会翻倍,因此带来的问题是超时周期可能相对较长。如果发送方一个接一个发送分组1、2、3、4、5,假设分组2丢失了,分组1、3、4、5先到达,则会引发接收方的第二件事,接收方会发送由3、4、5引起的三个冗余ACK1。TCP规定,一旦收到3个冗余ACK,就执行快速重传,即在该报文段的定时器过期之前重传丢失的报文段。

        TCP和GBN、选择重传的异同。TCP在累计确认ACK时与GBN相似,但是发生超时时,不会像GBN那样重新发送所有未确认分组,而是只发送最开始的那个未确认分组。 TCP允许接收方有选择地确认接收失序报文段,而不是像选择重传那样丢弃。

对比三者:

1.GBN

        应用层申请发送5个数据,然后此时窗口都是空闲,发送方就唰唰挨个发了1、2、3、4、5个分组出去,并在发送分组1时启动计时,接收方接收到分组1、2、3后发送ACK1、2、3给发送方,发送方收到ACK3后,就明白接收方已经接收到了分组3及之前的分组,由于还有4、5未确认收到,因此重新开始计时,如果4在给发送途中丢失了,那么5先到达了接收方,接收方就会把5给丢了,然后重新发送ACK3给发送方,告诉发送方我上一次收到的还是ACK3,然后发送方并不会立即发送分组4给它,而是等待计时器超时,才重新发分组4和分组5。

2.选择重传

        应用层申请发送5个数据,然后此时窗口都是空闲,发送方就唰唰挨个发了1、2、3、4、5个分组出去,并在发送分组1时启动计时,假设到达接收方的顺序是2、4、1、5,接收方就挨个发送ACK2、4、1、5给发送方,由于不是累计确认,因此发送方只是知道了接收方收到了这四个分组,并把这四个分组的计时器给关了,由于分组3丢了,发送方迟迟未收到ACK3,因此会重传分组3,注意,只是重传分组3而已。

3.TCP

         应用层申请发送5个数据,然后此时窗口都是空闲,发送方就唰唰挨个发了1、2、3、4、5个分组出去,并在发送分组1时启动计时,分组2传输过程中丢了,然后1、3、4、5都到了,接收方收到1时就返回ACK2给发送方,告诉发送方我下一个要的是分组2,当收到3、4、5时也不会把它们丢掉,而是存起来,并且每收到一个就发送一个ACK2给发送方,告诉它我要的是分组2!此时发送方收到了4个分组2,由于冗余,就不必等待计时器超时,直接重传分组2给接收方。接收方收到后就返回ACK6给发送方。

5.5 流量控制

        流量控制的原因。前面讲过,一条TCP连接的每一侧主机都为该连接设置了接收缓存。当该TCP连接收到正确、按序的字节后,它就将数据放入接收缓存(放入缓存中即可向发送方发送ACK)。相关联的应用进程会从该缓存中读取数据,但不必是数据刚一到达就立即读取。事实上,接收方应用也许正忙于其他任务,甚至要过很长时间后才去读取该数据。如果某应用程序读取数据时相对缓慢,而发送方发送得太多、太快,发送的数据就会很容易地使该连接的接收缓存溢出,TCP为它的应用程序提供了流量控制服务以消除发送方使接收方缓存溢岀的可能性。

        流量控制是什么。流量控制因此是一个速度匹配服务,即发送方的发送速率与接收方应用程序的读取速率相匹配。

        流量控制的具体实现。本次讨论假设TCP接收方丢弃失序的报文段。TCP让发送方维护一个接收窗口,接收窗口用于使发送方知道接收方还有多少可用的缓存空间,由于TCP是全双工连接,因此连接双方的发送方都有一个接收窗口。假设主机A向主机B发送一个大文件,主机B为该连接分配了一个接收缓存,并用RcvBuffer来表示其大小,主机B上的进程会不时地从缓存中读取数据我们定义一下变量:

  • LastByteRead:主机B的应用进程最后从缓存中读出的数据流的最后一个字节的编号。
  • LastByteRcvd:从网络中到达并且已放入主机B接收缓存中的数据流的最后一个字节编号。

        如果TCP不允许缓存溢出,则必须满足LastByteRcvd-LastByteRead≤RcvBuffer。接收窗口用rwnd来表示,rwnd=RcvBuffer-[LastByteRcvd-LastByteRead]

        由于主机A是发送方,尽管A知道rwnd的大小,但A应不应该发送分组给B还由另外一个因素影响,即发送出去但B未接受到的分组个数,那么A需要跟踪两个变量LastByteSent和LastByteAcked,LastByteSent-LastByteAcked就是主机A发送出去但未被确认接收的分组个数。通过将未确认的数据量控制在值rwnd以内,就可以保证主机A不会使主机B的接收缓存溢出。因此,主机A在该连接的整个生命周期须保证:LastByteSent-LastByteAcked≤rwnd

        这个方案目前为止还存在一个问题,先给出两个假设,假设主机B的接收缓存已经存满,使得rwnd = 0,在将rwnd = 0通告给主机A之后,还要假设主机B没有任何数据要发给主机A。此时,考虑会发生什么情况。因为主机B上的应用进程将缓存清空,TCP并不向主机A发送带有rwnd新值的新报文段;事实上,TCP仅当在它有数据或有确认要发时才会发送报文段给主机A。这样,主机A不可能知道主机B的接收缓存已经有新的空间了,即主机A被阻塞而不能再发送数据!为了解决这个问题,TCP规范中要求:当主机B的接收窗口为0时,主机A继续发送只有一个字节数据的报文段。这些报文段将会被接收方确认。最终缓存将开始清空,并且确认报文里将包含一个非0的rwnd 值。

        UDP在“流量控制”的表现。UDP并不提供流量控制,报文段由于缓存溢出可能在接收方丢失。例如,考虑一下从主机A上的一个进程向主机B上的一个进程发送一系列UDP报文段的情形。对于一个典型的UDP实现,UDP将在一个有限大小的缓存中加上报文段,该缓存在相应套接字(进程的门户)“之前”。进程每次从缓存中读取一个完整的报文段。如果进程从缓存中读取报文段的速度不够快,那么缓存将会溢出,并且将丢失报文段。

5.6 TCP连接管理

        1.连接步骤

        假设运行在一台主机(客户)上的一个进程想与另一台主机(服务器)上的一个进程建立一条连接。客户应用进程首先通知客户TCP,它想建立一个与服务器上某个进程之间的连接。客户中的TCP会用以下方式与服务器中的TCP建立一条TCP连接:

  • 第一步:客户端的TCP向服务器端的TCP发送一个特殊的TCP报文,该报文不包含应用层数据,但是在报文的首部中的一个标志为(即SYN比特)被置为1。因此这个特殊的报文段被称为SYN报文段。同时客户会随机地(目的是为了避免某些攻击)选择一个初始序号(client_isn)放置在该报文的序号字段中,然后该报文被封装到一个IP数据报中并发送给服务器。
  • 第二步:一旦包含TCP SYN报文段的IP数据到达服务器主机,服务器会从该数据中提取出TCP SYN报文段,并为该TCP连接分配TCP缓存和变量,同时向该客户TCP发送允许连接的报文段。这个允许连接的报文段也不包含应用层数据。但是,在报文段的首部却包含3个重要的信息。首先,SYN比特被置为1。其次,该TCP报文段首部的确认号字段被置为client _ isn +1。最后,服务器选择自己的初始序号 (server_isn),并将其放置到TCP报文段首部的序号字段中。这个允许连接的报文段实际上表明了:“我收到了你发起建立连接的SYN分组,该分组带有初始序号clientjsno我同意建立该连接。我自己的初始序号是serverjsnon该允许连接的报文段被称为SYNACK报文段SYNACK segment)。
  • 第三步:在收到SYNACK报文段后,客户也要给该连接分配缓存和变量。客户主机则向服务器发送另外一个报文段;这最后一个报文段对服务器的允许连接的报文段进行了确认(该客户通过将值server_isn + 1放置到TCP报文段首部的确认字段中来完成此项工作)。因为连接已经建立了,所以该SYN比特被置为0。该三次握手的第三个阶段可以在报文段负载中携带客户到服务器的数据

        一旦完成这3个步骤,客户和服务器主机就可以相互发送包括数据的报文段了。在以后每一个报文段中,SYN比特都将被置为0。注意到为了创建该连接,在两台主机之间发送了 3个分组。(为什么需要初始序号?为什么需要3次握手,而不是两次握手?======未完待续)

        2.TCP拆除

        参与一条TCP连接的两个进程中的任何一个都能终止该连接。当连接结束后,主机中的“资源”(即缓存和变量)将被释放。举一个例子,假设某客户打算关闭连接,如图3-40所示。客户应用进程发出一个关闭连接命令。这会引起客户TCP向 服务器进程发送一个特殊的TCP报文段。 这个特殊的报文段让其首部中的一个标志位即FIN比特(参见图3-29)被设置为1。当服务器接收到该报文段后,就向发送方回送一个确认报文段。然后,服务器发送它自己的终止报文段,其FIN比特被置为1。最后该客户对这个服务器的终止报文段进行确认。此时,在两台主机上用于该连接的所有资源都被释放了。

客户:“还有没有事,老子要挂电话了”

服务器:“OK。我知道你要挂电话了”

服务器:“好了,我没事了,咱们可以挂电话了”

客户:“OK,我挂了” ...等待一会儿,如果服务器没吱声就说明服务器知道了,客户直接挂电话。

         一个客户端TCP协议的生命周期如下图:

        客户TCP开始时处于CLOSED (关闭)状态。 客户的应用程序发起一个新的TCP连接 (可通过在第2章讲过的Python例子中创建一个Socket对象来完成)。这引起客户中的TCP向服务器中的TCP发送一个SYN报文段。在发送过SYN报文段后,客户TCP进入了 SYN_SENT状态。当客户TCP处在SYN_SENT状态时,它等待来自服务器TCP的对客户所发报文段进行确认且SYN比特被置为1的一个报文段。收到这样一个报文段之后,客户TCP进入ESTABLISHED (已建立)状态。当处在ESTABLISHED状态时,TCP客户就能发送和接收包含有效载荷数据(即应用层产生的数据) 的TCP报文段了。假设客户应用程序决定要关闭该连接。(注意到服务器也能选择关闭该连接。)这引起客户TCP发送一个带有FIN比特被置为1的TCP报文段,并进入FIN_WAIT_1状态。当处在FIN_WAIT_1状态时,客户TCP等待一个来自服务器的带有确认的TCP报文段。当它收到该报文段时,客户TCP进入FIN_WAIT_2状态。当处在FIN_WAIT_2状态时,客户等待 来自服务器的FIN比特被置为1的另一个报文段;当收到该报文段后,客户TCP对服务器的报文段进行确认,并进入TIME_WAIT状态。假定ACK丢失,TIME_WAIT状态使TCP客户重传最后的确认报文。TIME.WAIT状态中所消耗的时间是与具体实现有关的,而典型的值是30秒、1分钟或2分钟。 经过等待后,连接就正式关闭,客户端所有资源(包括端口号)将被释放。 

        3.SYN洪范攻击

        我们在TCP三次握手的讨论中已经看到,服务器为了响应一个收到的SYN,分配并初始化连接变量和缓存。然后服务器发送一个SYNACK进行响应,并等待来自客户的ACK报文段。如果某客户不发送ACK来完成该三次握手的第三步,最终(通常在一分 多钟之后)服务器将终止该半开连接并回收资源。

        SYN洪范攻击指的是攻击者发送大量的TCP SYN报文段,而不完成第三次握手,由于服务器接收到大量的SYN报文段,因此它得不断地为这些半开连接分配资源,导致服务器的连接资源被消耗殆尽。

        防御方法——SYN cookie。当服务器接收到一个SYN报文段时,它并不知道该报文段是来自一个合法的用户,还是一个SYN洪泛攻击的一部分。因此服务器不会为该报文段生成一个半开连接。相反,服务器生成一个初始TCP序列号,该序列号是SYN报文段的源和目的IP地址与端口号以及仅有该服务器知道的秘密数的一个复杂函数(散列函数)(因此服务器可以根据合法客户端返回的包含该函数的报文进行定位该客户端)。这种精心制作的初始序列号被称为“cookie”。服务器则发送具有这种特殊初始序列号的SYNACK分组。重要的是,服务器并不记忆该cookie或任何对应于SYN的其他状态信息(因此不会消耗服务器资源)。

        4.如果服务器不在线怎么办

        目的主机通过发送一个RST标志位为1的特殊报文段给源,告诉它咱没有那个服务。

5.7 拥塞控制原理

        1.拥塞控制和流量控制比较

        流量控制关注的是一对双方之间,即控制双方的传输接受速率,避免接收方的接收缓存不够装下发送方发来的报文段,是“私人”的。拥塞控制关注的是整个网络系统的关系,避免某个人把网络堵了大家都用不了。

        2.拥塞的原因及代价

        原因①及代价:假设路由器缓存无限大,则当发送方的发送速率十分接近路由器的带宽,即流量强度接近于1,分组会经历巨大的排队延迟导致网络拥塞。为什么流量强度为 1 时排队时延是无穷(而不是一个定值)? - 知乎

        原因②及代价:假设路由器缓存有限且是可靠连接,那么当达到路由器缓存上限且发送方还在传时,会导致拥塞,并且会发生丢包事件,一旦发生丢包事件,则发送方必须重传该报文段,重复则会导致资源浪费(原本一次可以搞定的事情,非要多次)。而且如果发生了拥塞事件,发送方也许会发生超时并重传在队列中因拥塞而被推迟但还未丢失的分组,造成资源浪费。

        原因③及代价:假设有4个发送方A、B、C、D和多台有限缓存的路由器a、b、c、d,假设此时A给C发送报文路上经过a、c两个路由器,B给C发送报文路上经过b、c两个路由器,由于c是A和B共享的路由器,那么二者在同时发送给C时就会为c上有限的缓存空间而竞争,如果先是A发给C,当A发给C的报文段越来越多,占据了c缓存的绝大部分,此时尽管B也给C发报文,但由于c的空闲缓存几乎被A占满,因此B发的报文段几乎在c处就夭折了,尽管这些报文段经过了路由器b,那也是白经过了。由此看出,当一个分组沿一条路径被丢弃时(拥塞会导致此发生),每个上游路由器用于转发该分组到丢弃该分组而时使用的传输容量最终被浪费掉了。

        3.拥塞控制方法

        ①端到端拥塞控制。在端到端拥塞控制方法中,网络层没有为运输层拥塞控制提供显示支持,TCP采用该种方法。

        ②网络辅助的拥塞控制。在网络辅助的拥塞控制中,路由器向发送方提供关于网络中拥塞状态的显式反馈信息。这种反馈可以简单地用一个比特来指示链路中的拥塞情况。在这种方法中,拥塞信息从网络反馈到发送方通常有两种方式。方式一是网络路由器直接反馈信息给发送方,这种方式的通知通常采用了一种阻塞分组(choke packet) 的形式(主要是说:“我拥塞了!”)。方式二是在分组从发送方发往接收方的途中,路由器通过标记或更新该分组中的某个字段,来指示拥塞是否产生,一旦接收方收到一个标记的分组后,接收方就会向发送方通知该网络拥塞指示,这种方式至少要经过一个完整的RTT。

        4.TCP拥塞控制

        TCP通过让发送方根据其感知到的网络拥塞程度来限制其能向连接发送流量的速率。但是存在三个问题:①发送方如何限制发送速率?②发送方如何感知?③发送方感知到端到端的拥塞时,如何改变发送速率?

        问题①:运行在发送方的TCP拥塞控制机制跟踪一个额外的变量,即拥塞窗口 。拥塞窗口表示为cwnd,它对一个TCP发送方能向网络中发送流量的速率进行了限制。特别是,在一个发送方中未被确认的数据量不会超过cwnd与rwnd中的最小值,即上面的限制条件允许发送方向该连接发送cwnd个字节的数据,在该RTT结束时发送方接收对数据的确认报文。因此,该发送方的发送速率大概是cwnd/RTT字节/秒。通过调节cwnd的值,发送方因此能调整它向连接发送数据的速率 。

        问题②:TCP下发送方认为是发生丢包事件有两种情况,一种是超时,另一种是收到来自接收方的三个冗余ACK。当发送方察觉到丢包事件时,则认为在发送方到接收方的路径上出现了拥塞的指示。

        问题③:发送方根据收到ACK来进行发送速率的调整,及cwnd大小的调整。

详解问题③:

a.慢启动

        慢启动是起点低,启动快。当TCP刚建立连接时,cwnd的值通常初始设置为一个MSS(最大报文段)的较小值,这就使得初始发送速率为MSS/RTT,远小于可用宽带。在慢启动状态,cwnd的值以1个MSS开始并且每当传输的报文段首次被确认就增加1个MSS,比如,第一个RTT由于cwnd只有1MSS,因此只发送一个报文段,第一个RTT结束接收到一个报文段,因此cwnd增长为2MSS,由于cwnd为2MSS,因此第二个RTT发送两个报文段,第二个RTT结束就收到2个报文段,cwnd=2+2=4,呈指数增长。

         什么时候结束这种指数性增长呢?有三种情况,第一种情况是因为发送超时而引起的丢包(注意,此处指的丢包不一定真的丢包,只是发送方认为丢包,下同),如果发生了这种情况TCP发送方将cwnd设置为1,并且设置一个状态变量ssthresh,使其等于发生丢包时的窗口大小的一半,即cwnd/2,然后重新开始慢启动;第二种情况是因为收到三个冗余ACK而引起的丢包,如果发生了这种情况,也会将ssthresh同上设置,但cwnd的值设置为ssthresh+收到冗余ACK个数的MSS,然后进入拥塞避免状态执行cwnd线性增长;第三种情况是当cwnd达到或超过ssthresh的值时,结束慢启动进入拥塞避免状态执行cwnd线性增长。

b.拥塞避免

        一旦进入拥塞避免状态,cwnd的值大约是上次遇到拥塞时的值的一半,即距离拥塞可能并不遥远。在这个状态每个RTT只将cwnd的值增加一个MSS(无论这个RTT内收到多少个ACK),TCP发送方无论何时到达一个新的确认,就将cwnd增加一个 MSS ( MSS/cwnd)字节。例如,如果MSS是1460字节并且cwnd是14 600字节,则在一个R1T内发送10个报文段。每个到达ACK (假定每个报文段一个ACK)增加1/10MSS的拥塞窗口长度,因此在收到对所有10个报文段的确认后,拥塞窗口的值将增加了一个 MSS。

        什么时候结束线性增长呢?发生a的前两种情况。        前八个RTT内Tahoe和Reno动作相同。在第八回合时,Tahoe由于超时引起丢包,则将阈值ssthresh设置为12/2=6后,将cwnd设置为1重新进行慢启动,到达阈值后进入拥塞避免状态;在第八回合时,Reno收到3个冗余ACK引起丢包,则将阈值ssthresh设置为12/2=6后,将cwnd设置为6+3=9后进入拥塞避免状态。

        5.TCP性能分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值