运输层端口号,复用与分用的概念
运输层的任务是直接为应用进程间的逻辑通信提供服务,运输层使用端口来区分不同的应用进程
运行在计算机上的进程使用进行标识符PID来标志,但是因特网上的计算机并不是使用统一的操作系统,不同的操作系统又使用不同格式的进程标识符,为了使运行不同操作系统的计算机的应用进程之间能够进行网络通信,就必须使用统一的方法对TCP/IP体系的应用进程进行标识,TCP/IP体系的运输层使用端口号来区分应用层的不同应用进程,端口号使用16比特来表示,取值范围是0~65535,可以分为以下三类
- 熟知端口号:0~1023,IANA把这些端口号指派给了TCP/IP体系中最重要的一些应用协议,例如FTP使用21/20,HTTP使用80,DNS使用53
- 登记端口号:1024~49151,为没有熟知端口号的应用程序使用,使用这类端口号必须在IANA按照规定的手续登记,以防止重复,例如微软远程桌面使用的端口是3389
- 短暂端口号:49152~65535,留给客户进程选择暂时使用,当服务器进程收到客户进程的报文时,就知道了客户所使用的动态端口号,通信结束后,这个端口号可供其他客户进程以后使用
端口号只具有本地意义,即端口号只是为了标识本计算机应用层中的各进程,在因特网中,不同计算机中的相同端口号是没有任何联系的
发送方的复用和接收方的分用
上图是收发双方的应用进程,发送方的某些应用进程所发送的不同应用报文,在运输层使用UDP协议进行封装,这称为UDP复用,而另一些应用进程所发送的不同应用报文,在运输层视同TCP协议进行封装,这称为TCP复用

运输层使用端口号来区分不同的应用进程, 不管是使用运输层的UDP协议封装成的UDP用户数据报还是使用TCP协议封装成的TCP报文段,在网络层都需要使用IP协议封装成IP数据报,这称为IP复用

IP数据报中协议字段的值,用来表明IP数据报的数据载荷部分封装的是何种协议数据单元,取值为6表示封装的是TCP报文段,取值为17,表示封装的是UDP用户数据报

接收方的网络层收到IP数据报后进行IP分用

若IP数据报首部中协议字段的值为17,则把IP数据报的数据载荷部分所封装的UDP用户数据报上交运输层UDP,若协议字段的值为6,则把IP数据报的数据载荷部分所封装的TCP报文段上交运输层的TCP

运输层对UDP用户数据报进行UDP分用,对TCP报文段进行TCP分用,也就是根据端口号,将他们交付给上层相应的应用进程

应用层常用协议

不管在运输层使用TCP协议还是UDP协议,在网络层都需要使用IP协议

端口号的作用

上图中用户PC,DNS服务器,Web服务器用过交换机进行互连,他们处于同一个以太网中,并且DNS服务中记录有www.porttest.com对应的IP地址,在用户PC中使用网页浏览器来访问Web服务器的内容,在网页中输入Web服务器的域名,用户PC中的DNS客户端进程会发送一个DNS查询请求报文,其内容为“域名www.porttest.com所对应的IP地址是什么?“,DNS查询请求报文需要使用运输层的UDP协议封装成UDP用户数据报,其首部中的源端口字段的值在短暂端口号49151~65535中挑选一个未被占用的用来表示DNS客户端进程,例如49152,目的端口字段的值设置为53(这是DNS服务端进程所使用的熟知端口号)

之后,将UDP用户数据报封装在IP数据报中,通过以太网发送给DNS服务器

DNS服务器端收到该数据报后,从中解封出UDP用户数据报,UDP首部中的目的端口号为53,这表明应将该UDP用户数据报的数据载荷部分也就是DNS查询请求报文交付给服务器中的DNS服务器端进程

DNS服务器端进程解析DNS查询请求报文的内容,然后按其要求查找对应的IP地址,之后会给用户PC发送DNS响应报文,其内容为”域名www.porttest.com所对应的IP地址为192.168.0.3“,DNS响应报文需要使用运输层的UDP协议,封装成UDP用户数据报,其首部中的源端口字段的值设置为熟知端口中的端口号53,表明这是DNS服务器端进程所发送的UDP用户数据报,目的端口字段的值设置为49152,这是之前用户PC中发送DNS查询请求报文的DNS客户端进程所使用的短暂端口号

之后,将UDP用户数据报封装在IP数据报中,通过以太网发送给用户PC

用户PC收到该数据报后,从中解封出UDP用户数据报,UDP首部中的目的端口号为49152,这表明应将UDP用户数据报的数据载荷部分也就是DNS响应报文交付给用户PC中的DNS客户端进程

DNS客户端进程解析DNS响应报文的内容,就可知道自己之前所请求的Web服务器的域名所对应的IP地址为192.168.0.3,现在用户PC中的HTTP客户端进程可以向Web服务器发送HTTP请求报文了,其内容为首页内容是什么?

HTTP请求报文需要使用运输层的TCP协议封装成TCP报文段,其首部中的源端口字段的值,在短暂端口49151~65535中挑选一个未被占用的用来表示HTTP客户端进程,例如之前使用过的49152,目的端口字段的值设置为80,这是HTTP服务器端进程所使用的熟知端口号

之后,将TCP报文段封装在IP数据报中,通过以太网发送给Web服务器

Web服务器收到该数据报后,从中解封出TCP报文段,TCP首部中的目的端口号为80,这表明应将该TCP报文段的数据载荷部分也就是HTTP请求报文交付给本服务器中的HTTP服务器端进程

HTTP服务器端进程解析HTTP请求报文的内容,然后按其要求查找首页内容,之后会给用户PC发送HTTP响应报文,其内容是HTTP客户端所请求的首页内容

HTTP响应报文需要使用运输层的TCP协议封装成TCP报文段,其首部中的源端口字段的值设置为熟知端口80,目的端口设置为49152

之后将TCP报文段封装在IP数据报中通过以太网发送给用户PC

用户PC收到该数据报后,从中解析出TCP报文段,TCP首部中的目的端口号为49152,这表明应将该TCP报文段的数据载荷部分也就是HTTP响应报文交付给用户PC中的HTTP客户端进程

HTTP客户端进程解析HTTP响应报文的内容,并在网页浏览器中进行显示,这样我们就可以在网页浏览器中看到Web服务器所提供的首页内容了

UDP和TCP对比
UDP的全称是用户数据协议(User Datagram Protocol),TCP的全称是传输控制协议TCP(Transmission Control Protocol)

上图中使用UDP传输数据纵坐标为时间,使用UDP协议的通信双方,可以随时发送数据
在使用TCP协议的情况下,使用TCP协议的通信双方在进行数据传输之前,必须使用三报文握手来建立TCP链接,TCP连接建立成功后才能进行数据传输,传输结束后,必须使用四报文挥手来释放TCP连接
三报文握手和四报文挥手属于TCP的连接管理,这里指的连接是指逻辑连接关系,并不是物理连接


上图中是使用UDP协议进行通信的四台主机,其中任何一台主机都可向其他三台主机发送广播,也可以向某个多播组发送多播,还可以向某台主机发送单播


从上图可以看出,UDP支持单播,多播以及广播,也就是UDP支持一对一,一对多以及一对全的通信
使用TCP进行通信时,使用TCP协议的通信双方,在进行数据传输之前,必须使用三报文握手来建立TCP链接,TCP连接建立成功后,通信双方之间就好像有一条可靠的通信信道,通信双方使用这条基于TCP连接的可靠信道进行通信

从上图中可以看出,TCP仅支持单播,也就是一对一的通信
对应用报文的处理
使用UDP的情况下,发送方的应用进程将应用层报文交付给运输层UDP

UDP直接给应用层报文添加一个UDP首部,使之成为UDP用户数据报,然后进行发送

接收方的UDP收到该UDP用户数据报后,去掉UDP首部,将应用层报文交付给应用进程

也就是说UDP对应用进程交下来的报文既不合并也不拆分,而是保留这些报文的边界

UDP是面向报文的
下面是使用TCP的情况
发送方的TCP把应用进程交付下来的数据块仅仅看做是一连串的无结构的字节流,TCP并不知道这些传送的字节流的含义,仅将他们编号并存储在自己的发送缓存中

TCP根据发送策略,从发送缓存中提取一定数量的字节构建TCP报文段并发送

接收方的TCP,一方面从所接收到的TCP报文段中取出数据载荷部分并存储在接收缓存中,一方面将接收缓存中的一些字节交付给应用进程

TCP不保证接收方应用进程所收到的数据块与发送方应用进程所发出的数据块具有对应大小的关系,例如发送方应用进程交给发送方的TCP共10个数据块,但接收方的TCP可能只用了4个数据块,就把收到的字节流交付给上层的应用进程,但接收方应用进程收到的字节流必须和发送方应用进程发出的字节流完全一样,但是接收方的应用进程必须有能力识别收到的字节流,把它还原成有意义的应用层数据

上图中,在实际的网络中,基于TCP连接的两端,可以同时进行TCP报文段的发送和接收,也就是全双工通信,一个TCP报文段包含上千个字节是很正常的
从上面可以看出,TCP是面向字节流的,这正是TCP实现可靠传输,流量控制,以及拥塞控制的基础
提供的服务不同
当运输层使用UDP协议时,向其上层提供的是无连接的不可靠的传输服务,发送方给接收方发送UDP用户数据报,若传输过程中用户数据报受到干扰而产生误码,接收方UDP可以通过该数据报首部中的校验码和字段的值,检查出产生误码的情况,但仅仅丢弃该数据报,其他什么也不做,发送方给接收方发送UDP用户数据报,如果该数据报被因特网中的某个路由器丢弃了,发送方UDP不做任何处理,因为UDP向上层提供的是无连接不可靠的传输服务,因此对于UDP用户数据报出现的误码和丢失等问题,UDP并不关心。基于UDP的这个特点,UDP适用于实时应用,例如IP电话,视频会议等
使用TCP协议时,尽管网际层中的IP协议向上层提供的是无连接不可靠的传输服务,也就是说IP数据报可能在传输过程中出现误码,丢弃,但只要运输层使用TCP协议,就可向器上层提供面向连接的可靠传输服务,可以想象成,使用TCP协议的收发双方基于TCP连接的可靠信道进行数据传输,不会出现误码,丢失,乱序以及重复等传输差错,TCP适用于要求可靠传输的应用,比如文件传输

协议的首部
一个UDP用户数据报由首部和数据载荷两部分构成,其首部格式如下图,仅有4个字段,每个字段长度为2个字节,由于UDP不提供可靠传输服务,它仅仅在网际层的基础上,添加了用于区分应用进程的端口,因此其首部非常简单,仅仅8个字节
一个TCP报文段由首部和数据载荷两部分组成,其首部格式比UDP复杂多,其最小长度为20字节,最大长度为60字节,这是因为TCP要实现可靠传输,流量控制,拥塞控制等服务,其首部就很复杂

TCP流量控制
如果在发送数据的是偶,发送方把数据发送得过快,接收方可能来不及接收,这就会造成数据的丢失,流量控制(flow control)就是让发送方的发送速率不要过快,要让接收方来得及接收,利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制

例如上图中,这是因特网上的两台主机,他们之间已经建立了链接,A给B发送数据,B对A进行流量控制,假设下图是A待发送的字节数据,如果主机A发送的每个TCP数据报文段可携带100字节数据,因此图中每个小格子表示100个字节数据的序号

在主机A和B建立TCP连接时,B告诉A:”我的接收窗口为400“,因此,主机A将自己的发送窗口也设置为400,这意味着主机A在未收到主机B发来的确认时,可将序号落入发送窗口中的全部数据发送出去

主机B对A的流量控制的过程为,主机A将发送窗口内序号1~100的数据,封装成一个TCP报文段发送出去,发送窗口内还有300字节可以发送

上图中的seq是TCP报文段首部中的序号字段,取值为1表示TCP报文段数据载荷的第一个字节的序号是1,这里的DATA表示这是一个TCP数据报文段,主机A将发送窗口内序号101~200的数据,封装成一个TCP报文段发送出去,发送窗口内还有200字节可以发送

主机A将发送窗口201~300的数据封装成一个TCP报文段发送出去,但该报文段在传输过程中丢失了,主机A发送窗口内还有100字节可以发送

主机B对主机A所发送的201号以前的数据进行累计确认,并在该累计确认中将窗口字段的值调整为300

也就是对主机A进行流量控制,上图中的大写ACK是TCP报文段首部中的标志位,取值1表示这是一个TCP确认报文段,小写ack是TCP报文段首部中的确认号字段,取值201表示序号201之前的数据已全部正确接收,现在希望收到序号201及其后续数据。rwnd是TCP报文段首部中的窗口字段,取值300表示自己的接收窗口为300。
主机A收到该累计确认后,将发送窗口向前滑动,使已发送收到确认的这些数据的序号移出发送窗口

由于主机B在该累计确认中将自己的接收窗口调整为300,所以主机A也将自己的发送窗口调整为300,目前,主机A发送窗口内的序号为201500,也就是主机A还可以发送这300字节,其中201300号字节是已发送的数据,若重传计时器超时,他们会被重传,301400号字节以及401500号字节还未发送,可被分别封装在一个TCP报文段中发送。
主机A现在可将发送缓存中1200的字节数据全部删除了,因为这部分的数据是已经确认发送成功的,主机A将发送窗口内序号301400的数据封装成一个TCP报文段发送出去,发送窗口内还有100字节可以发送

主机A将发送窗口内序号401~500的数据封装成一个TCP报文段发送出去,至此序号落在发送窗口内的数据已全部发送出去了

不能再发送新数据,现在发送窗口内序号201~300这100字节数据的重传计时器超时了,主机A将他们重新封装成一个TCP报文段发送出去,暂时不能发送其他数据

主机B收到该重传的TCP报文段后,对主机A所发送的501号以前的数据进行累计确认,并在该累计确认中将发送窗口的值调整为100,这是主机B对主机A进行的第二次流量控制

主机A收到该累计确认后,将发送窗口向前滑动,使已发送并收到确认的这些数据的序号移出发送窗口

由于主机B在累计确认中将自己的接收窗口调整为了100,因此,主机A也相应的将自己的发送窗口调整为100,目前,主机A发送窗口内的序号为501~600,也就是主机A还可以发送这100字节

主机A现在可将发送缓存中序号201500的字节数据全部删除,因为已经收到了主机B对他们的累计确认,主机A将发送窗口内的501600的数据封装成一个TCP报文段发送出去,至此,序号落在发送窗口内的数据已经全部发送出去了,不能再发送新数据了

主机B对主机A所发送的601号以前的数据进行累计确认,并在该累计确认中将窗口字段的值调整为0

这是主机B对主机A进行的第三次流量控制,主机A收到该累计确认后,将发送窗口向前滑动,使已发送并收到确认的这些数据的序号移出发送窗口,由于主机B在该累计确认中将自己的接收窗口调整为了0

因此,主机A相应的将自己的发送窗口调整为0,目前主机A不能再发送一般的TCP报文段了,主机A现在可将发送缓存中序号501~600的字节数据全部删除了,因为已经收到了主机B对他们的累计确认

假设主机B向主机A发送了零窗口的报文段后不久,主机B的接收缓存又有了一些存储空间,于是主机B向主机A发送了接收窗口等于300的报文段,然后,这个报文段在传输过程中丢失了

主机A一直等待主机B发送的非零窗口的通知,而主机B也一直等待主机A发送的数据

如果不采取措施,这种互相等待而形成的死锁局面将一直持续下去

为了解决这个问题,TCP为每一个连接设有一个持续计时器,只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器,若持续计时器超时,就发送一个零窗口探测报文,仅携带1字节数据,而对方在确认这个探测报文段时,给出自己现在的接收窗口值,如果接收窗口仍然为0,那么收到这个报文段的一方就重新启动持续计时器,如果接收窗口不是0,那么死锁的局面就可以被打破了
在上图中,主机A收到零窗口通知时,就启动一个持续计时器,当持续计时器超时时,主机A立刻发送一个仅携带1字节数据的零窗口探测报文段,假设主机B此时的接收窗口又为0

主机B就在确认这个零窗口探测报文段时,给出自己现在的接收窗口值为0,主机A再次收到零窗口通知,就再次启动一个持续计数器,当持续计时器超时时,主机A立刻发送一个零窗口探测报文段

假设主机B此时的接收缓存又有了一些存储空间,于是将自己的接收窗口调整为300,主机B就在确认这个零窗口探测报文段时,给出自己现在的接收窗口值为300,这样就打破了死锁的局面

问题:主机A向主机B发送零窗口探测报文段时,如果主机B此时的接收窗口仍然为0,那么主机B根本就无法接受该报文段,又怎么会针对该报文段给主机A发回确认?
答:实际上TCP规定即接收窗口为0,也必须接受零窗口探测报文段,确认该报文段,以及携带有紧急数据的报文段
问题:如果零窗口探测报文段丢失了,会出现什么问题?还能否打破死锁局面
答:零窗口探测报文段也有重传计时器,当重传计时器超时后,零窗口探测报文段会被重传
TCP的拥塞控制
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏,这种情况就叫做拥塞(congestion)
在计算机网络找那个的链路容量(带宽),交换结点中的缓存和处理机等,都是网络的资源,若出现拥塞而不进行控制整个网络的吞吐量将随输入负荷的增大而下降

输入负载:代表单位时间内输入给网络的分组数量
吞吐量:代表单位时间内从网络输出的分组数量
具有理想拥塞控制的网络,在吞吐量达到饱和之前,网络吞吐量应等于所输入的负载,所以吞吐量曲线是45度的斜线,但当输入负载超过某一限度时,由于网络资源受限,吞吐量就不再增加而保持水平线,也就是吞吐量达到饱和,这就表明输入的负载中有一部分损失掉了,例如输入到网络中的某些分组被某个节点丢弃了。
在这种理想的拥塞控制下,网络的吞吐量仍然维持在其所能达到的最大值,但是实际情况就和这个不同
对于下面这种曲线,随着输入负载的增加,网络吞吐量的增长率逐渐减小,也就是在网络吞吐量还未达到饱和时,就已经有一部分的输入分组被丢弃了

当网络的吞吐量明显地小于理想的吞吐量时,网络就进入了轻度拥塞的状态

值得注意的是,当输入负载到达某一数值时,网络的吞吐量反而随着输入负载的增大而减小,这时网络就进入了拥塞状态

当负载继续增大到某一数值时,网络的吞吐量就减小为0,此时网络就无法工作了,这就是所谓的死锁

因此,必须对网络进行拥塞控制,实际的拥塞控制曲线应尽量接近理想的拥塞控制曲线

四种拥塞控制算法
- 慢开始
- 拥塞避免
- 快重传
- 块恢复
以下是对这四种算法的理解,但是对于下面的网络,都假定满足以下条件,方便进行分析
- 数据是单方向传送,而另一个方向只传送确认
- 接收方总是有足够大的缓存空间,因而发送方发送窗口的大小由网络的拥塞程度来决定
- 以最大报文段MSS的个数为讨论问题的单位,而不是以字节为单位
超时重传时间的选择
主机A和主机B已经建立TCP连接,如果主机A给主机B发送TCP数据报文段0,并记录下当前的时间,主机B收到后,给主机A发送相应的确认报文段,主机A收到确认报文段后,记录下当前的时间,那么主机A记录下的这两个时间,他们的差值就是报文段的往返时间RTT

由于这是第0个报文段的RTT,可以使用RTT0来表示,如果将超时重传时间RTO的值,设置得比RTT0的值小,这样就会引起报文段不必要的重传,使网络负荷增大
如果将超时重传时间RTO的值设置得远大于RTT0的值,那么这样会使得重传推迟的时间太长

使网络的空闲时间增大,降低了传输效率

超时重传时间RTO的值应该设置为略大于报文段往返时间RTT的值
但是真实情况比上述更复杂,TCP下层是复杂的互联网环境,主机A所发送的报文段可能只经过一个高速率的局域网,也有可能经过多个低速率的网络,并且每个IP数据报的转发路由还可能不同
比如主机A个主机B发送TCP数据报文段1,主机B收到后,给主机A发送相应的确认报文段,主机A这次测得的报文段往返时间RTT1如下图所示

显然RTT1远大于RTT0,如果超时重传时间RTO还是我们之前所确定的略大于RTT0的话,这对于数据报文段1是不合适的

会造成该报文段不必要的重传

我们不能使用某次测量得到的RTT样本来计算超时重传时间RTO,但是我们可以利用每次测量得到的RTT样本,计算加权平均往返时间RTTs,这样就可以得到比较平滑的往返时间

超时重传时间RTO的值应略大于加权平均往返时间RTT
建议使用的超时重传时间

上述公式中的RTTs是加权平均往返时间,RTTd是RTT偏差的加权平均
RTTd的计算方法
当测量到第一个RTT样本时,RTTd的值取为该样本值的一半,以后,每测量到一个RTT样本时,都按该公式计算新的RTTd的值
RTT往返时间的测量

上面网络中,主机A给主机B发送TCP数据报文段,但该报文段在传输过程中丢失了,当超时重传计时器超时后,主机A就重传该报文段,主机B收到后,给主机A发送确认报文段
主机A收到该确认报文段后,无法判断该报文段是对原报文段的确认还是对重传报文段的确认,该报文段实际上是对重传报文段的确认,也就是正确的RTT应该是重传TCP数据报文段这段时间(下图箭头处)

但是,如果主机A误将该确认当做是对原报文段的确认,也就是误认为是下图箭头这段时间

那么计算出的RTTs和RTO就会偏大,降低了传输效率
另外一种情况,主机A给主机B发送TCP数据报文段,主机B收到后,给主机A发送确认报文段,由于某种原因,该确认报文段没有在正常时间内到达主机A,这会导致主机A对之前所发送的数据报文段的超时重传

那么主机A收到迟到的确认报文段后无法判断该报文段是对原报文段的确认还是对重传报文段的确认,该报文段实际上是对原报文段的确认,正确的时间如下图

但是如果主机A误将该确认当做是对重传报文段的确认,那么所计算出的RTTs和RTO就会偏小,会导致报文段没有必要的重传,增大网络负荷,错误时间如下图

当发送方出现超时重传后,收到确认报文段时,是无法判断出该确认到底是对原报文段的确认还是对重传报文段的确认
解决上述问题的做法是

TCP超时重传时间的计算



TCP可靠传输的实现
TCP基于以字节为单位的滑动窗口来实现可靠传输
下图是因特网上的两台主机,他们之间已经建立了TCP连接,

现在假设发送方收到了一个来自接收方的确认报文段,在报文段首部中的窗口字段的值为20,也就是接收方表明自己的接收窗口的尺寸为20字节,确认字段的值为31,这表明接收方希望收到下一个数据的序号为31,而序号30为止的数据已经全部正确接收了,因此发送方根据这两个字段的值构造出自己的发送窗口

由于本例中接收方告诉发送方自己的接收窗口尺寸为20,因此发送方将自己的发送窗口尺寸也设置为20,发送方在没有收到接收方确认的情况下,可以把发送窗口内的数据依次全部发送出去,凡是已经发送过的数据,在未收到确认之前,都必须暂时停留,以便在超时重传时使用。

上图展示的是发送窗口的前沿和后沿,发送窗口后沿的后面部分,是已发送并已收到确认的数据字节的序号,这些数据字节显然不需要再保存在发送缓存中,可以直接将他们删除,发送窗口前沿前面的部分是当前不允许发送的数据字节的序号。

发送窗口后沿的移动情况
- 不动,没有收到新的确认
- 前移,收到了新的确认
发送窗口的后沿不可能向后移动,因为不能撤销掉已收到的确认
发送窗口前沿的移动情况
- 通常是不断向前踢动的
- 不动
- 没有收到新的确认,对方通知的窗口大小也不变
- 收到新确认单双方通知的窗口缩小,使发送窗口前沿正好不动
- 向后收缩,对方通知的窗口缩小了
但是TCP标准不建议向后收缩,因为很有可能发送方在收到这个通知之前,就已经发送了窗口中的许多数据,现在又要收缩窗口,不让发送这些数据,就会产生错误
现在假定发送方将发送窗口内序号3141的数据封装在几个不同的报文段中发送出去,此时发送窗口的位置并没有变化,发送窗口内序号3141的数据已经发送但未收到确认,而序号42~50的数据是允许发送但还未发送的

问题:如何描述发送窗口的状态?
-
答:可以使用三个指针p1,p2,p3分别指向相应的字节序号,这样小于P1的就是已发送并已收到确认的部分,大于等于P3的是不允许发送的部分,P3-P1可以得出当前发送窗口的尺寸,P2-P1可以得出已发送但尚未收到确认的字节数量,P3-P2可以得出允许发送但当前尚未发送的字节数量

接收方的接收窗口,其值为20,在接收窗口外面到30号为止的数据,是已经发送过相应确认并已交付给应用进程的数据,因此无需再保留这些数据,可将他们从接收缓存中删除,接收窗口内31~50号数据是允许接收的数据,接收窗口外51号及其后续数据,目前不允许接收

假设发送方之前发送的封装有32和33号数据的报文段到达了接收方

由于数据序号落在接收窗口内,所以接收方接受他们,并将他们存入接收缓存,但是,他们是未按序到达的数据,因为31号数据还未到达,这有可能是丢了,也有可能是滞留在网络中某处

接收方只能对按序收到的数据中的最高序号给出确认
所以接收方发出的确认报文段中的确认序号仍然是31,也就是希望收到31号数据

窗口字段的值仍是20,表明接收方没有改变自己接收窗口的大小,发送方收到确认报文段后,发现这是针对31号数据的重复确认,就知道接收方收到了未按序到达的数据,由于这是针对31号数据的第一个重复确认,因此这并不会引起发送方针对该数据的快重传,另外,接收方通知的窗口尺寸仍是20,因此发送方仍保持自己的发送窗口尺寸为20,现在假设封装有31号数据的报文段到达了接收方,接收方接收该报文段

将其封装的31号数据存入接收缓存

接收方现在可将接收到的31~33号数据交付给应用进程,然后将接收窗口向前移动3个序号,并给发送方发送确认报文段

该确认报文段中窗口字段的值仍为20,表明接收方没有改变自己接收窗口的大小,确认号字段的值为34,这表明接收方已经收到了序号33为止的全部数据

现在,假设又有几个数据报文段到达了接收方,他们封装有37,38以及40号数据,这些数据的序号虽然落在接收窗口内,但他们都是未按序到达的数据,只能先暂存在接收缓存中

假设接收方先前发送的确认报文段到达了发送方,发送方接收后,将发送窗口向前滑动3个序号

发送窗口的尺寸保持不变,这样就有新序号5153落入发送窗口内,而序号3133移出了发送窗口,现在可将31~33号数据从发送缓存中删除了

发送方继续将发送窗口内序号42~53的数据,封装在几个不同的报文段中发送出去,现在发送窗口内的序号已经用完了,发送方在未收到接收方发来的确认的情况下,不能再发送新的数据

序号落在发送窗口内的已发送数据,如果迟迟收不到接收方的确认,则会产生超时重传




TCP的运输链接管理
TCP的连接建立
TCP是面向连接的协议,它基于运输连接来传送TCP报文段,它连接的建立和释放是每一次面向连接的通信中必不可少的过程,存在三个阶段:建立TCP连接,数据传送,释放TCP连接
其中一台主机中的某个应用进程主动发起TCP连接建立称为TCP客户,另一台主机中被动等待TCP连接建立的应用进程称为TCP服务器,他们之间连接的过程可以称之为握手,握手需要在TCP客户和服务器之间交换三个TCP报文段
-
在开始之初,两端的TCP进程都处于关闭状态,一开始TCP服务器进程首先创建数据控制块,用来存储TCP连接中的一些重要信息,例如TCP链接表等,然后就准备接受TCP客户进程的连接请求,此时TCP服务器进程就进入监听状态,等待TCP客户进程的连接请求

TCP服务器进程是被动等待来自TCP客户进程的连接请求,而不是主动发起,因此称为
被动打开连接
TCP客户进程也是首先创建传输控制块,然后在打算建立TCP连接时,向TCP服务器进程发送TCP连接请求报文段,并进入
同步已发送状态。

SYN:Synchronize Sequence Numbers,同步序列编号
TCP连接请求报文段首部中的同步位SYN被设置为1,表明这是一个TCP连接请求报文段,序号字段seq被设置为一个初始值x,作为TCP客户进程所选择的初始序号(就像上述中可靠连接的实现,发送数据,需要在发送窗口内的数据才被发送,这里的序号就是那个,这里的初始值x表示当前发送的TCP报文段所处的序号)
TCP规定SYN被设置为1的报文段不能携带数据,但要消耗掉一个序号
这个过程是由TCP客户主动发起的,因此称为主动打开连接

TCP服务器进程收到TCP连接请求报文段后,如果同意建立连接,则向TCP客户进程发送TCP连接请求确认报文段,并进入同步已接收状态

报文段首部中同步位SYN和确认位ACK都设置为1,表明这是一个TCP连接请求确认报文段,序号字段seq被设置了一个初始值y作为TCP服务器进程所选择的初始序号,确认号字段ack的值被设置成了
x+1,这是对TCP客户进程所选择的初始序号的确认
这是确认报文段也不能携带数据,因为它是SYN被设置为1的报文段,但同样要消耗掉一个序号

TCP客户进程收到TCP连接请求确认报文后,还要向TCP服务器进程发送一个普通的TCP确认报文段,并进入连接已建立状态
报文段首部中的确认位ACK被设置为1,表明这是一个普通的TCP确认报文段,序号字段seq被设置为
x+1,这是因为TCP客户进程发送的第一个TCP报文段的序号为x,并且不携带数据,因此第二个报文段的序号为x+1(和TCP发送建立连接的报文段处于连续位置),确认号字段ack被设置为y+1,这是对TCP服务器进程所选择的初始序号的确认TCP规定普通的TCP确认报文段可以携带数据,但如果不携带数据,则不会消耗序号,在这种情况下,所发送的下一个数据报字段的序号仍是
x+1

TCP服务器进程收到该确认报文段后也进入连接已建立状态,现在TCP双方都进入了连接已建立状态,他们可以基于已建立好的TCP连接进行可靠的数据传输了

问题:为什么TCP客户进程最后还要发送一个普通的TCP确认报文段?能够使用”两报文握手“建立连接呢?
-
答:TCP客户进程发出一个TCP连接请求报文段,但该报文段在某些网络结点长时间滞留了,这必然会造成该报文段的超时重传,假设重传的报文段被TCP服务器进程正确接收,TCP服务器进程给TCP客户进程发送一个TCP连接请求确认报文段并进入
连接已建立状态,由于我们改为两报文握手,因此TCP服务器进程发送完TCP连接请求确认报文段后,就进入了”连接已建立“状态,而不像三报文握手那样进入同步已接收状态,并等待TCP客户进程发来针对TCP连接请求确认报文段的普通确认报文段。TCP客户进程收到TCP连接请求确认报文段后,进入TCP连接已建立状态,但不会给TCP服务器进程发送针对该报文段的普通确认报文段,现在,TCP双方都处于连接已建立状态,他们可以相互传输数据,之后可以通过四报文挥手来释放连接,TCP双方都进入了关闭状态。一段时间后,之前滞留在网络中的那个失效的TCP连接请求报文段到达了TCP服务器进程,TCP服务器进程会误认为这是TCP客户进程,又发起了一个新的TCP连接请求,于是给TCP客户进程发送TCP连接请求确认报文段,并进入连接已建立状态,该报文段到达TCP客户进程,由于TCP客户进程并没有发起新的TCP连接请求,并且处于关闭状态,因此不会理会该报文段,但TCP服务器进程已进入连接已建立状态,它认为新的TCP连接已建立好了,并一直等待TCP客户进程发来数据,这将白白浪费TCP服务器进程所在主机的很多资源

采用三报文握手而不是两报文握手来建立TCP连接,是为了防止已失效的连接请求报文段突然又传送到TCP服务器进程,因而导致错误

TCP连接的释放
数据传输结束后,TCP通信双方都可以释放连接,现在TCP客户进程和TCP服务器进程都处于连接已建立状态,假设使用TCP客户进程的应用进程通知其主动关闭TCP连接,TCP客户进程会发送TCP连接释放报文段,并进入终止等待1状态

该报文段首部中的终止位FIN和确认位ACK的值都被设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认,序号seq字段的值设置为u,它等于TCP客户进程之前已传送过的数据最后一个字节的序号加1,确认号ack字段的值设置为v,它等于TCP客户进程之前已收到的数据的最后一个字节的序号加1
TCP规定终止位FIN等于1的报文段即使不携带数据,也要消耗掉一个序号
TCP服务器进程收到TCP连接释放报文段后,会发送一个普通的TCP确认报文段并进入关闭等待状态
报文段首部中的确认位ACK的值被设置为1,表明这是一个普通的TCP确认报文段,序号seq字段的值设置为v,它等于TCP服务器进程之前已传送过的数据的最后一个字节的序号加1,这与之前收到的TCP连接释放报文段中的确认号匹配,确认号ack字段的值设置为
u+1,这是对TCP连接释放报文段的确认

TCP服务器进程这时应通知高层应用程序:TCP客户进程要断开与自己的TCP连接,此时,从TCP客户进程到TCP服务器进程这个方向的连接就释放了,这时的TCP连接属于半关闭状态,也就是TCP客户进程已经没有数据要发送了,但TCP服务器进程如果还有数据要发送,TCP客户进程仍要接收,也就是说,TCP服务器进程到TCP客户端进程这个方向的连接并未关闭。这个状态可能会持续一段时间,TCP客户进程收到TCP确认报文段后就进入终止等待2状态,等待TCP服务器进程发出的TCP连接释放报文段,若使用TCP服务器进程的应用程序已经没有数据要发送了,应用进程就通知其TCP服务器进程释放连接

由于TCP连接是由TCP客户进程主动发起的,因此TCP服务器进程对TCP连接的释放称为被动关闭连接,TCP服务器进程发送TCP连接释放报文段并进入最后确认状态

该报文段首部中的终止位FIN和确认位ACK的值都必须被设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认,现在假定序号seq字段的值为w,这是因为在半关闭状态下,TCP服务器进程可能又发送一些数据

确认号ack字段的值为u+1,这是对之前收到的TCP连接释放报文段的重复确认

TCP客户进程收到TCP连接释放报文段后,必须针对该报文段发送普通的TCP确认报文段,之后进入时间等待状态,该报文段首部中的确认位ACK的值被设置为1,表明这是一个普通的TCP确认报文段,序号seq字段的值设置为u+1,这是因为TCP客户进程之前发送的TCP连接释放报文段虽然不携带数据,但要消耗掉一个序号,确认号ack字段的值设置为w+1,这是对所收到的TCP连接释放报文段的确认

TCP服务器进程收到该报文段后就进入关闭状态,而TCP客户进程还要经过2MSL后才能进入关闭状态(MSL:最长报文段寿命,建议为2分钟)

也就是说TCP客户进程进入时间等待状态后,还要经过4分钟才能进入关闭状态
问题:TCP客户进程发送完最后一个确认报文段后,为什么不直接进入关闭状态,而是要进入时间等待状态,2MSL后才进入关闭状态,是否有这个必要?
-
答:TCP服务器进程发送TCP连接释放报文段后,进入最后确认状态,TCP客户进程收到该报文段后,发送普通的TCP确认报文段,并进入关闭状态而不是时间等待状态,然而该TCP确认报文段丢失了,这必然会造成TCP服务器进程对之前所发送的TCP连接释放报文段的超时重传并仍处于最后确认状态

重传的TCP连接释放报文段到达TCP客户进程,由于TCP客户进程属于关闭状态,因此不理睬该报文段,这必然会造成TCP服务器进程反复重传TCP连接释放报文段,并一直处于最后确认状态而无法进入关闭状态

所以时间等待状态以及处于该状态2MSL时长,可以确保TCP服务器进程可以收到最后一个TCP确认报文段而进入关闭状态,TCP客户进程在发送完最后一个TCP确认报文段后,再经过2MSL时长,就可以使本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的TCP连接中不会出现旧连接中的报文段
保活计时器
TCP双方已经建立连接,如果TCP客户进程所在的主机突然出现了故障,那么TCP服务器进程以后就不能再收到TCP客户端进程发来的数据, 可以使用保活计时器来解决使TCP服务器进程不要白白等下去,TCP服务器进程每收到一次TCP客户进程的数据,就重新设置并启动保活计时器(2小时定时),若保活计时器定时周期内未收到TCP客户进程发来的数据,则当保活计时器到时后,TCP服务器进程就向TCP客户进程发送一个探测报文段,以后每隔75秒发送一次,若一连发送10个探测报文段后仍无TCP客户进程的响应,TCP服务器进程就认为TCP客户进程所在主机出了故障,接着就关闭这个连接