网络层(上篇):https://blog.csdn.net/pcwl1206/article/details/83999363
网络层(下篇):https://blog.csdn.net/pcwl1206/article/details/84098381
第5章:运输层
本章重点:
1、运输层为相互通信的应用进程提供逻辑通信;
2、端口和套接字的意义;
3、无连接的UDP的特点;
4、面向连接的TCP的特点;
5、在不可靠的网络上实现可靠传输的工作原理,停止等待协议和ARQ协议;
6、TCP的滑动窗口、流量控制、拥塞控制和连接管理。
写在前面:
1、运输层两个协议的应用场景:
TCP:分段、编号、流量控制、建立会话。比如:QQ传文件(需要分段传输)、访问网站、FTP、HTTP
UDP:一个数据报就能完成数据通信、不建立会话、多播。比如:QQ聊天、电视台节目(多播)
2、传输层和应用层之间的关系:一个运输层协议 + 端口 = 一个应用
http = TCP + 80 https = TCP + 443 ftp = TCP + 21
SMTP = TCP + 25 POP3 = TCP + 110 RDP = TCP + 3389
共享文件夹 = TCP + 445 SQL = TCP + 1433 DNS = UDP + 53 or TCP + 53(很少的时候)
3、应用层协议和服务之间的关系:
服务(对外提供的服务:Web、FTP、SMTP等)运行后,在TCP或UDP的某个端口来监听客户端的请求。
查看自己计算机监听的端口:netstat -an
4、Windows防火墙的作用:只开必要的端口。
运输层是整个网络体系中的关键层次之一。一定要弄清楚以下一些重要的概念:
1、运输层为相互通信的应用进程提供逻辑通信;
2、端口和套接字的意义;
3、无连接的UDP的特点;
4、面向连接的TCP的特点;
5、在不可靠的网络上实现可靠传输的工作原理,停止等待协议和ARQ协议;
6、TCP的滑动窗口、流量控制、拥塞控制和连接管理。
5.1 运输层协议概述
5.1.1 进程之间的通信
从通信和信息处理的角度看,运输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是用户功能中的最低层。当网络的边缘部分中的两台主机使用网络的核心部分的功能进行端到端的通信时,只有主机的协议栈才有运输层,而网络核心部分中的路由器在转发分组时都只用到下三层的功能。
为什么需要运输层?
首先明确一个问题,两台主机通信时,真正进行通信的是两台主机中的进程。网络层指定明确这两台主机的IP地址,但是分组还停留在网络层而没有交付到主机中的应用进程,所以需要运输层支持进程与进程之间的数据交换。
如上图所示,主机A的应用进程AP1和主机B的应用进程AP3通信,同时,主机A的应用进程AP2和主机B的应用进程AP4进行通信。这表明运输层有一个很重要的功能:复用和分用。这里的“复用”是指在发送方不同的应用进程都可以使用同一个运输层协议传送数据,而“分用”是指接收方的运输层在剥去报文的首部后能够把这些数据正确交付目的应用进程。
图中的逻辑通信:指的是从应用层来看,只要把应用层报文交给下面的运输层,运输层就可以把这报文传送到对方的运输层,这种通信就像是水平方向直接传送数据。
网络层和运输层的区别:网络层为主机之间提供逻辑通信,而运输层为应用进程之间提供端到端的逻辑通信。
5.1.2 运输层的两个主要协议
1、用户数据报协议UDP(User Datagram Protocol)
2、传输控制协议TCP(Transmission Control Protocol)
运输层中的这两种协议在TCP/IP协议栈中的位置如下图所示:
按照OSI术语,两个对等运输实体在通信时传送的数据单元叫做运输协议单元TPDU(Transport Protocol Data Unit)。但在TCP/IP体系中,则根据所使用的协议是TCP还是UDP,分别称之为TCP报文段和UDP用户数据报。
UDP的特点:UDP在传送数据之前不需要建立连接,远地主机的运输层在收到UDP报文后,也不需要给出任何确认。虽然UDP是不可靠交付,但在某些情况下UDP确是一种最有效的工作方式。
TCP的特点:提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP不提供广播或多播服务。由于TCP要提供可靠的、面向连接的运输服务,而不可避免地增加了许多开销,如:确认、流量控制、计时器以及连接管理等。这不仅使协议单元的首部增大了很多,还要占用许多处理机资源。
下表中列出了一些应用层协议使用的运输层协议(UDP/TCP):
应用 | 应用层协议 | 运输层协议 |
---|---|---|
名字转换 | DNS(域名系统) | UDP |
文件传输 | TFTP(简单文件传送协议) | UDP |
路由选择协议 | RIP(路由信息协议) | UDP |
IP地址配置 | DHCP(动态主机配置协议) | UDP |
网络管理 | SNMP(简单网络管理协议) | UDP |
远程文件服务器 | NFS(网络文件系统) | UDP |
IP电话 | 专用协议 | UDP |
流式多媒体通信 | 专用协议 | UDP |
多播 | IGMP(网际组管理协议) | UDP |
电子邮件 | SMTP(简单邮件传输协议) | TCP |
远程终端接入 | TELNET(远程终端协议) | TCP |
万维网 | HTTP(超文本传输协议) | TCP |
文件传送 | FTP(文件传送协议) | TCP |
5.1.3 运输层的端口
应用层的所有应用进程都可以通过运输层再传送到网络层,这就是复用。运输层从网络层收到发送给各应用进程的数据后,必须分别交付指明的各应用进程,这就是分用。显然,给应用层的每个应用进程赋予一个非常明确的标志至关重要。那么运输层采用的则是协议端口号(protocol port number),或者简称端口(port)。
虽然通信的终点是应用进程,但只要把所传送的报文交到目的主机的某个合适的端口,剩下的工作(即交付给目的进程)就由TCP或UDP来完成。
TCP/IP的运输层用一个16位端口号来标志一个端口。但是需要注意的是端口号只具有本地意义。16位的端口号可允许有65535个不同的端口号。
互联网上的计算机通信是采用客户-服务器方式。客户在发起通信请求时,必须先知道对方服务器的IP地址和端口号。因此运输层的端口号分为下面两大类:服务器端使用的端口号和客户端使用的端口号。
1、服务器端使用的端口号又分为两类:
(1)熟知端口号/系统端口号: 数值为0~1023
(2)登记端口号:1024~49151
2、客户端使用的端口号:49152~65535
5.2 用户数据报协议UDP
5.2.1 UDP概述 ---- 重要【UDP的特点】
用户数据报协议UDP只在IP的数据报服务之上增加 了很少的一点功能,这就是复用和分用功能以及差错检测功能。UDP的主要特点如下:
1、UDP是无连接的,即发送数据之前不需要建立连接,因此减少了开销和发送数据之前的时延;
2、UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的连接状态表;
3、UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界;
4、UDP没有拥塞控制,因此网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用是很重要的。很多实时应用(如IP电话、实时视频会议等)要求源主机以恒定的速率发送数据,并且允许在网络发生拥塞时丢失一些数据,但却不允许数据有太大的时延。UDP正好适合这种要求;
5、UDP支持一对一、一对多、多对一和多对多的交互通信;
6、UDP的首部开销较小,只有8字节,比TCP的20字节的首部要短。
5.2.2 UDP的首部格式
如上图所示:用户数据报UDP有两个字段组成:首部字段和数据字段。
首部字段比较简单,一共就8个字节,由四段组成,每个字段都是两个字节。各字段的意义如下:
1、源端口:源端口号,在需要对方回信时选用,不需要时可用全0;
2、目的端口:目的端口号,这在终点交付报文时必须使用;
3、长度:UDP用户数据报的长度,其最小值是8(仅有首部);
4、检验和:检验UDP用户数据报在传输过程中是否有错,有错就丢弃。
这里需要注意的两点是:
1、虽然在UDP之间的通信使用到其端口号,但是由于UDP的通信是无连接的,因此不需要使用套接字(TCP之间的通信必须要在两个套接字之间建立连接)。
2、IP数据报的检验和只检验IP数据报的首部,但UDP的检验和是把首部和数据部分一起都检验。
5.3 传输控制协议TCP概述 ---- 重要
TCP的三个重点问题:
1、TCP协议如何实现可靠传输?
2、TCP协议如何实现流量控制?
3、TCP协议如何避免网络拥塞?
5.3.1 TCP最主要的特点 ---- 十分重要
TCP是TCP/IP体系中非常复杂的一个协议,下面介绍TCP最主要的特点:
1、TCP是面向连接的运输层协议。即应用程序在使用TCP协议之前,必须先建立TCP连接。在传送数据完毕后,也必须释放连接;
2、每一条TCP连接只能有两个端点(endpoint),每一条TCP连接只能是点对点的(一对一);
3、TCP提供可靠交付的服务。通过TCP连接传送数据,无差错、不丢失、不重复,并且按序到达;
4、TCP提供全双工通信(即:可同时接和收)。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把接收到的数据放入缓存,上层应用进程在合适的时候读取缓存中的数据;
5、面向字节流。TCP中的“流”(stream)指的是流入到进程或从进程流出的字节序列。“面向字节流”的含义是:虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据仅仅看成是一连串的无结构数据流。TCP并不知道所传送的字节流的含义。TCP不保证接收方应用程序所收到的数据块和发送方应用程序所发送的数据块具有对应大小的关系(例如:发送方应用程序交给发送方的TCP共10个数据块,但接收方的TCP可能只用了4个数据块就把收到的字节流交付上层的应用程序中)。但接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全一样。
TCP报文段先要传送到IP层,加上IP首部后,再传送到数据链路层,再加上数据链路层的首部和尾部后,才离开主机发送到物理链路。
TCP并不关心应用程序一次把多长的报文发送到TCP的缓存中,而是根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少字节(UDP发送的报文长度是应用进程给出的,一次发送完)。如果应用进程传送到TCP缓存的数据块太长,TCP就可以把它划分短一些再传送。如果应用进程一次只发来一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。
5.3.2 TCP的连接
TCP把连接作为最基本的抽象。
每一条TCP连接有两个端点,它们叫套接字(socket)或插口。端口号拼接到IP地址后面就构成了套接字。如下所示:
套接字 socket = (IP地址: 端口号)
每一条TCP连接唯一地被通信两端的两个端点(即两个套接字)所确定:即
TCP连接 ::= {socket1, socket2} = {(IP1: port1), (IP2: port2)}
同一个IP地址可以有多个不同的TCP连接,而同一个端口号也可以出现在多个不同的TCP连接中。
5.4 可靠传输的工作原理
TCP发送的报文段是交给IP层传送的。但是IP层只能提供尽最大努力交付,也就是说,TCP下面的网络所提供的是不可靠的传输。因此,TCP必须采用适当的措施使得两个运输层之间的通信变得可靠。
理想的传输条件有以下两个特点:
1、传输信道不产生差错;
2、不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。
然而实际的网络都不具备以上两个理想条件。但我们可以使用一些可靠传输协议,当出现差错时让发送方重传出现差错的数据,同时在接收方来不及处理收到的数据,及时告诉发送方适当降低发送数据的速度。
简单的说,实现可靠性传输就是:只要你没告诉我,你收到了。那么我就认为你没收到,我就会给你重传。
5.4.1 停止等待协议
说明:该协议并未在运输层中使用,只是为了引出可靠性传输的问题。
“停止等待”就是每发送完一个分组(数据单元)就停止发送,等待对方的确认,在收到确认后再发送下一个分组。
在“停止等待”这个过程中会出现四种情况:无差错情况、出现差错,超时重传、确认丢失和确认迟到。
1、无差错情况
如下图a所示:A发送分组M1,发完就暂停发送,等待B的确认。B收到了M1就向A发送确认。A在收到了对M1的确认后,就再发送下一个分组M2。同样,在收到B对M2的确认后,再发送M3。
2、出现差错,超时重传
如上图b所示:B接收M1时检测出了差错,就丢弃M1,其他什么也不做(不通知A收到有差错的分组)。也有可能是M1在传输过程中丢失了,这时B当然什么也不知道。在这两种情况下,B都不会向A发送任何消息。
可靠性传输协议是这样设计的:A只要超过了一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,再重发前面发送过的分组。这就叫做超时重传。所以要在每发送完一个分组时设置一个超时计时器。
这里应该注意以下三点:
1、A发送完一个分组后,必须暂时保留已发送的分组的副本,在发生超时重传时使用;
2、分组和确认分组都必须进行编号。这样才能明确哪一个发送出去的分组收到了确认,哪一个没收到确认;
3、超时计时器设置的重传时间应当比数据在分组传输的平均往返时间更长一些。
3、确认丢失
如下图a中所示,B发送的对M1的确认丢失了。A在设定的超时重传时间内没有收到确认,并无法知道是自己发送的分组出错、丢失,或者是B发送的确认丢失了。因此A在超时计数器到期后就重传M1。这时B又收到了重传的分组M1,这时采取两个行动:
1、丢弃这个重复的分组M1,不向上层交付;
2、向A发送确认。不能认为已经发送过确认就不再发送了,因为A之所以重传M1,就表示A没有收到对M1的确认(虽然A不知道是自己发送的分组出错、丢失,或者是B发送的确认丢失了,但是B知道是它发送的确认丢失了)
4、确认迟到
如上图b所示。传输过程中并没有差错,但是B对分组M1的确认迟到了。A会收到重复的确认(因为超时重传M1后,会收到确认,而且这个确认在第一次M1确认的前面)。对重复的确认,A收下后就丢弃。B仍然会收到重复的M1,并且同样丢弃,并重传确认分组。
上诉的四种情况中使用的确认和重传机制,我们就可以在不可靠的网络上实现可靠的通信。这种可靠性传输协议称为自动重传请求ARQ(Automatic Repeat Request)。
5、信道利用率
停止等待协议的优点是简单,但缺点是信道利用率太低。
如上图所示:其中TD代表A发送分组所需的时间,TA代表B发送确认分组所需的时间,RTT是往返时间。则发送一个分组需要(TD + RTT + TA),但是仅仅只有TD时间内才用来传送数据。信道利用率U的计算公式如下:
为了提高传输效率,发送方可以不使用低效率的等待协议,而是采用流水线传输。流水线传输就是发送方可以连续发送多个分组,不必每发送完一个分组就停下来等待对方的确认。这样可以使信道上一直有数据不间断地在传送。
当使用流水线传输时,就要用到下面介绍的连续ARQ协议和滑动窗口协议。
5.4.2 连续ARQ协议
滑动窗口协议比较复杂,也是TCP协议的精髓所在,后面会细讲。这里先给出连续ARQ协议最基本的概念。
如下图所示,发送方维持的发送窗口,它的意义是:位于发送窗口内的5个分组都可连续发送出去,而不需要等待对方的确认。这样,信道利用率就提高了。
连续ARQ协议规定,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。
接收方一般都采用累计确认的方式。也就是说,接收方不必对收到的分组逐个发送确认,而是在收到几个分组后,对按序到达的最后一个分组发送确认,这就表示接收方已经正确的收到的所有分组的信息。
例如:如果发送方发送了前5个分组,而中间的第3个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一遍。这就叫做Go-back-N(回退N),表示需要最退回来重传已经发送过的N个分组。
累积确认的优点:容易实现,即使确认丢失也不必重传;
累积确认的缺点:不能向发送方反映出接收方已经正确收到的所有分组信息。
5.5 TCP报文段的首部格式
TCP虽然是面向字节流的,但TCP传送的数据单元却是报文段。一个TCP报文段分为首部和数据两部分,而TCP的全部功能都体现在它的首部中各字段的作用。因此,只有弄清楚TCP首部各字段的作用才能掌握TCP的工作原理。
TCP报文段首部的前20个字节是固定的,后面有4n字节是根据需要而增加的选项。因此TCP首部的最小字段为20字节。
1、源端口和目标端口
各占2个字节,分别写入源端口和目标端口。和UDP一样,TCP的分用功能也是通过端口实现的。
2、序号
占4字节。在一个TCP连接中传送的字节流中的每一个字节都按照顺序标号。整个要传送的字节流的起始序号必须在连接建立时设置。首部中的序号字段值则指的是本报文段所发送的数据的第一个字节的序号。
3、确认号
占4字节,是期望收到对方下一个报文段的第一个数据字节的序号。需要记住:
若确认号 = N,则表明:到序号N-1为止的所有数据都已正确收到。
下图为XP向Web请求资源的过程,帮助理解序号和确认号。
4、数据偏移
占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。因为首部中有选项字段,所以首部的总长度并不固定(最大值:60字节,最小值20字节)。
5、保留
占6位,保留为以后用,目前值设置为0。
6、紧急UGR(URGent)
当UGR=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于优先级高的数据),而不是按照原来的排队顺序来传送。例如:取消运行很久的程序。
当UGR的值为1时,发送应用进程就告诉发送方的TCP有紧急数据要发送。于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍是普通数据。这时要与首部中紧急指针字段配合使用。
7、确认ACK(ACKnowledgment)
仅当ACK=1时,确认号字段才生效。当ACK=0时,确认号无效。TCP规定,在连接建立之后所有的报文段都必须把ACK置1。
8、推送PSH(PuSH)
当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。这种情况下,TCP就可以使用推送(push)操作。发送方TCP把PSH置1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快交付接收应用进程,而不再等整个缓存都填满了后再向上交付。
9、复位RST(ReSeT)
当RST=1时,表明TCP连接中出现严重错误,必须释放连接,然后再重新建立连接。
10、同步SYN(SYNchronization)
在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使用SYN=1和ACK=1。因此,SYN置为1就表示这是一个连接请求或连接接受报文。连接后数据传送过程SYN=0。
11、终止FIN(FINis)
用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已经发送完毕,并要求释放运输连接。
12、窗口Window
占2个字节。窗口指的是发送本报文段的一方的接收窗口(而不是自己的发送窗口)。窗口告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。
13、检验和
占2个字节。检验和字段检验的范围包括首部和数据这两部分。和UDP一样,在计算检验和时,需要加上TCP报文段前面的12个字节的伪首部。
14、紧急指针
占2个字节。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数的字节数。因此,紧急指针指出了紧急数据末尾在报文段中的位置。
15、选项
长度可变,最长可达40字节。当没有使用“选项”字段时,TCP的首部长度为20字节。
下面列举几个选项:
最大报文长度MSS(Maximum Segment Size):TCP报文段中数据字段的最大值。默认值是536字节。
窗口扩大:扩大窗口;
时间戳:计算往返时间RTT以及用于处理TCP序号超过2^32的情况,防止序号绕回。
.................等等
5.6 TCP可靠传输的实现 ---- 重要
5.6.1 以字节为单位的滑动窗口
这部分内容可以看书《计算机网络》的221~223,发送窗口和接收窗口之间的数据传输,由于过程比较繁琐,就不再此做详细记录。这里仅记录下关键知识点。
TCP的滑动窗口是以字节为单位的。
要描述一个发送窗口A的状态需要三个指针P1、P2和P3。它们的意义分别为:
1、小于P1的是已发送并已收到确认的部分,而大于P3的是不允许发送的部分;
2、P3 - P2 = A的发送窗口;
3、P2 - P1 = 已发送但未收到确认的字节数;
4、P3 - P2 = 允许发送但当前未发送的字节数(又称为可用窗口或有效窗口)。
接收窗口B只能对按序收到的数据中的最高序号给出确认。
下面来看看TCP的缓存和窗口的关系:
发送缓存用来暂时存放:
1、发送应用程序传送给发送方TCP准备发送的数据;
2、TCP已发送出去但尚未收到确认的数据。
发送窗口通常只是发送缓存的一部分。已被确认的数据应当从发送缓存中删除。发送应用程序必须控制写入缓存的速率,不能太快,否则发送缓存就会没有存放数据的空间。
接受缓存用来暂时存放:
1、按序到达的、但尚未被接收应用程序读取的数据;
2、未按序到达的数据。
如果接收应用程序来不及读取收到的数据,接收缓存最终会被填满,使接收窗口减小到零。反之,如果接收应用程序能够及时从接收缓存中读取收到的数据,接收窗口就可以增大,但最大不能超过接收缓存的大小。
- 结合上面的讨论,需要强调三点:
- 虽然A的发送窗口是根据B的接收窗口设置的,但在同一时刻,A的发送窗口并不总是和B的接收窗口一样大。需要考虑网络拥塞和通过网络传送窗口产生的时间滞后;
- 对于不按序到达的数据先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按顺序交付上层的应用程序;
- TCP要求接收方必须有累积确认功能,可以减小传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息捎带上。
5.6.2 超时重传时间的选择
TCP的发送方在规定的时间内没有收到确认就要重传已发送的报文段。
由于TCP的下层是互联网环境,发送的报文段可能只经过一个高速率的局域网,也可能经过多个低速率的网络,并且每个IP数据报所选择的路由还可能不同。如果把超时重传时间设置得太短,就会引起很多报文段的不必要重传,使网络负荷增大。但若把超时重传时间设置得过长,则又使网络的空闲时间增大,降低了传输效率。
上诉公式中的RTO即为超时重传时间,RTTS为报文段的加权平均往返时间,RTTD是RTT的偏差的加权平均值。公式了解即可。
但是重传报文段的时候会有一个问题:如何判定此确认报文段是对先发送的报文段的确认,还是对后来重传的报文段的确认?这对设置超时重传时间有很大的影响。
在计算加权平均RTTS时,只要报文段重传了,就不采用其往返时间。这样得出的加权平均RTTS和RTO就比较准确。
5.6.3 选择确认SACK
若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,我们可以利用选择确认SACK只传送缺少的数据而不重传已经正确到达接收方的数据。
如果这些字节的序号都在接收窗口之内,那么接收方就行先收下这些数据,但要把这些信息准确地告诉对方,使发送方不要再重复发送这些已经收到的数据。
如果要使用选择确认SACK,那么建立TCP连接时,就要在TCP首部的选项中加上“允许SACK”的选项,而双方必须事先商定好。
5.7 TCP的流量控制
5.7.1 利用滑动窗口实现流量控制
一般来说,我们总是希望数据传输的快一些。但如果发送方把数据发送得过快,接收方就可能来不及接收,这就造成数据的丢失。所谓流量控制(flow control)就是让发送方的发送速率不要太快,要让接收方来得及接收。
利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。
下面这个例子说明如何利用滑动窗口机制进行流量控制。
从图中可以看到B一共给A发送了3次rwnd(receiver window),即发送方的发送窗口不能超过接收方给出的接收窗口的数值。
图中大写的ACK表示首部中的确认位ACK,小写ack表示确认字段的值。
上诉案例中,接收方的主机B进行了三次流量控制。第一次把窗口减小到rwnd=300,第二次又减到rwnd=100,最后减到rwnd=0,即不允许发送方再发送数据了。这种使发送方暂停发送的状态将持续到主机B重新发出一个新的窗口值为止。
但是上诉过程有一个问题就是:B向A发送了一个零窗口,但是过了不久,B的接收缓存又有了一些存储空间。于是B向A发送了rwnd=400的报文段,但是这个报文段传输过程中丢了。A一直等待收到B发送的非零窗口的通知,而B也一直等待A发送的数据。如果没有其他措施,这种互相等待的死锁局面将一直延续下去。
为了解决上诉这个问题,TCP为每一个连接设有一个持续计数器(persistence timer)。只要TCP连接的一方收到对方的零窗口通知,就启动持续计数器。若持续计数器设置的时间到期,就发送一个零窗口探测报文段(仅携带1字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。如果窗口仍然是零,那么收到这个报文段的一方就重新设置持续计数器。如果窗口不是零,那么死锁的僵局就可以打破了。
5.7.2 TCP的传输效率
可以用不同的机制来控制TCP报文段的发送时机。例如:
第一种机制是:TCP维持一个变量,它等于最大报文段长度MSS。只要缓存中存放的数据达到MSS字节时,就组装成一个TCP报文段发出去。
第二种机制是:由发送方的应用进程指明要求发送报文段,即TCP支持的推送(push)操作;
第三种机制是:发送方的一个计时器期限到了,这时就把当前已有的缓存数据装入报文段(但长度不能超过MSS)发送出去。
Nagle算法:
若发送应用进程要把发送的数据逐个字节地送到TCP的发送缓存,则发送方就把第一个数据字节先发出去,把后面到达的数据字节都缓存起来。当发送方收到对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段发出去,同时继续对随后达到的数据进行缓存。只有在收到对前一个报文段的确认后才继续发送下一个报文段。当数据较快而网络速率较慢时,用这样的方法可明显地减少所用的网络带宽。Nagle算法还规定,当到达的数据已达到发送窗口大小的一半或已达到报文段的最大长度时,就立即发送一个报文段。
解决糊涂窗口综合征(设置过小的接收窗口):让接收方等待一段时间,使得或者接收缓存已有足够空间容纳一个最长的报文段,或者等到接收缓存已有一半空闲空间。只要出现这两种情况之一,接收方就发出确认报文,并向发送方通知当前的窗口大小。
上面两种方法可以配合使用,发送方不要发送很小的报文段的同时,接收方也不要在缓存刚刚有了一点小的空间就急忙把这个很小的窗口大小信息通知给对方。
5.8 TCP的拥塞控制 ---- 重要
这部分内容比较重要,书上讲的很详细,可以直接看书229~238页。
5.8.1 拥塞控制的原理
在计算机网络中的链路容量(即带宽)、交换结点中的缓存和处理机等,都是网络的资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫“拥塞”,出现拥塞的条件公式如下所示:
解决拥塞问题不能简单的增加一些资源,比如,把结点缓存的存储空间扩大,更换高速链路等等,许多情况下简单采用上诉做法都会引起一些列的其他问题,不但不能解决问题,还有可能使网络变得更坏。总之网络拥塞是一个多因素造成的结果,因此调整时也不能简单的考虑某一因素,要综合考虑各种因素。问题的实质往往是整个系统的各个部分不匹配,只有所有的部分都平衡了,问题才能得到解决。
拥塞控制:就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。
拥塞控制所要做的都有一个前提,就是网络能够承受现有网络的网络负荷。
- 流量控制与拥塞控制的区别:
流量控制:往往是指点到点通信的控制,是个端到端的问题(接收端控制发送端)。流量控制要做的就是抑制发送端发送数据的速率,以便接收端来得及接收。
拥塞控制:是一个全局性的过程,涉及到所有主机、所有的路由器、以及与降低网络传输性能有关的所有因素。
横坐标:提供的负载:代表单位时间内输入给网络的分组数目;
纵坐标:吞吐量:代表单位时间内从网络中输出的分组数目。
理想拥塞控制的网络,在吞吐量饱和之前,网络吞吐量应等于提供的负载。
实际的网络中,随着提供的负载的增大,网络吞吐量的增长速率逐渐减小,也就是说,在网络还没有达到饱和时,就已经有一部分的输入分组被丢弃了。
- 当网络的吞吐量明显地小于理想的吞吐量时,网络进入了轻度拥塞的状态;
- 当提供的负载达到某一数值时,网络的吞吐量反而随提供的负载的增大而下降,这时网络就进入了拥塞状态;
- 当提供的负载继续增大到某一数值时,网络的吞吐量就下降到零,网络已无法工作,这就是所谓的死锁状态。
开环控制和闭环控制:
- 开环控制:在设计网络时事先将有关发生拥塞的因素考虑周到,力求网络在工作时不产生拥塞。但一旦整个系统运行起来,就不再中途进行改正了;
- 闭环控制基于反馈环路的概念,主要有以下几种措施:
- 检测网络系统以便检测到拥塞何时、何处发生;
- 把拥塞发生的信息传送到可采取行动的地方;
- 调整网络系统的运行以解决出现的问题。
5.8.2 TCP的拥塞控制方法
TCP进行拥塞控制的算法有四种,即:慢开始、拥塞避免、快重传和快恢复。
下面要讨论的拥塞控制也叫做基于窗口的拥塞控制。为此,发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口。
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就可以再增大一些,以便把更多的分组发送出去,这样就可以提高网络的利用率。但只要网络出现拥塞或有可能出现拥塞,就必须把拥塞窗口减小一些,以减少注入到网络中的分组数,以缓解网络出现的拥塞。
发送方判断网络拥塞的依据就是出现了超时。
1、慢开始
慢开始算法的思路是:
当主机开始发送数据时,由于并不清楚网络的负荷情况,所以如果立即把大量数据字节注入到网络,那么就有可能引起网络发生拥塞。经验证明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是说由小到大逐渐增大拥塞窗口数值。
在每收到一个对新的报文段的确认后,可以把拥塞窗口增加多一个SMSS(最大报文段的数值:Sender Maximum Segment Size)的数值。更具体些,就是:
拥塞窗口cwnd每次的增加量 = min(N, SMSS)
N是原先未被确认,但现在被刚收到的确认报文段所确认的字节数。
每经过一个传输轮次,拥塞窗口cwnd就加倍。一个传输轮次所经历的时间其实就是往返时间RTT。
需要指明两点:
1、慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探下网络的拥塞情况),然后再逐渐增大cwnd。这当然比设置大的cwnd值一下子把许多报文段注入到网络中要“慢得多”。这对防止网络出现拥塞是一个非常好得方法。
2、图中是为了说明慢开始的原理。在TCP的实际运行中,发送方只要收到一个对新报文段的确认ACK,其拥塞窗口cwnd就立即加1,并可以发送新的报文段,而不需要等这个轮次中所有的确认都收到后,再发送新的报文段。
为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量,用法如下:
1、当cwnd < ssthresh时,使用上诉得慢开始算法;
2、当cwnd > ssthresh时,停止使用慢开始算法,而改用拥塞避免算法;
3、当cwnd = ssthresh时,既可以使用慢开始算法,也可以使用拥塞避免算法。
2、拥塞避免算法
拥塞避免的思路是:
让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是像慢开始那样加倍增长。因此在拥塞避免阶段就有“加法增大”AI(Additive Increase)的特点。这表明在拥塞避免阶段,拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
如下图所示:“拥塞避免”并非完全能够避免拥塞,只是把拥塞窗口控制为线性规律增长,使网络比较不容易出现拥塞。
当cwnd=16的的时候由慢开始进入拥塞避免,16是慢开始的门限值ssthresh;
当cwnd=24的时候,网络出现了超时,发送方判断为网络拥塞,调整门限值ssthresh=cwnd/2=12,同时设置拥塞窗口cwnd=1,进入慢开始阶段
当cwnd = ssthresh = 12时,改为执行拥塞避免算法,拥塞窗口按线性规律增大;
当cwnd=16的时候,发送方一连收到3个对同一报文段的重复确认。有时,个别报文段会在网络中丢失,但实际上网络并未发生拥塞。如果发送方迟迟收不到确认,就会产生超时,就会误认为网络中发生了拥塞,这就导致发送方错误地启动慢开始,把拥塞窗口cwnd又设置为了1,因而降低了传输效率。这个时候就需要使用快重传算法了。
3、快重传算法
快重传算法的思路是:
让发送方尽早知道发生了个别报文段的丢失。快重传算法首先要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认,即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认。
快重传算法规定:发送方只要一连收到3个重复确认,就知道接收方确实没有收到报文段,因而立即进行重传。这样就不会出现超时,发送方也不会认为出现了网络拥塞。
4、快恢复算法
图中的点4,发送方知道现在只是丢失了个别的报文段,于是不启动慢开始,而是执行快恢复算法。这时,发送方调整门限值ssthresh=cwnd / 2 = 8,同时设置拥塞窗口cwnd= ssthresh = 8,并开始进行拥塞避免算法。
也有的快恢复设置的cwnd = 新的ssthresh + 3 * MSS。因为既然发送方收到了3个重复的确认,就表明这三个分组已经离开了网络,可以把拥塞窗口扩大些。
5、AIMD算法
在拥塞避免阶段,拥塞窗口是按照线性规律增大的,这常称为加法增大AI。而一旦出现超时或3个重复的确认,就把门限值设置为当前拥有窗口值得一半,并大大减小拥塞窗口得值,这常称为“乘法减小”。二者合在一起就是AIMD算法。(和式增加,积式减少)。
完整的TCP拥塞控制的流程图如下所示:
下面还需要说明一点:
从接收方对发送方的流量控制的角度考虑,发送方的发送窗口一定不能超过对方给出的接收窗口值rwnd。
如果同时考虑拥塞控制和接收方对发送方的流量控制,发送方的窗口上限值应当取接收窗口rwnd和拥塞窗口cwnd这两个变量中较小的一个,即:
发送方窗口的上限值 = Min[rwnd, cwnd]
从这个公式可以得出以下结论:
- 当rwnd < cwnd时,是接收方的接收能力限制发送方窗口的最大值;
- 当rwnd > cwnd时,是网络的拥塞程度限制了发送方窗口的最大值。
也就是说,cwnd和rwnd中较小的那个,控制了发送方发送数据的速率。
5.8.3 主动队列管理AQM
上面讨论的TCP拥塞控制并没有和网络层采取的策略联系起来。其实,它们之间有着密切的关系。
网络层的策略对TCP拥塞控制影响最大的就是“先进先出“FIFO(First In First Out)的规则处理到来的分组。由于对列长度总是有限的,因此当队列已满时,以后再到达的所有分组(如果能继续排队,这些分组都将排在队列的尾部)将都被丢弃。这就叫做尾部丢弃策略。
路由器的尾部丢弃往往会导致一连串分组的丢失,这就使发送方出现超时重传,使TCP进入拥塞控制的慢开始状态,结果使TCP连接的发送方突然把数据的发送速率降低到很小的数值。更为严重的是,网络中存在很多的TCP连接,如果同一时间突然都进入到慢开始状态,称为全局同步。全局同步使全网的通信量下降很多,而在网络恢复正常后,其通信量又突然增大了很多。
为了避免发生网络中的全局同步现象,可以使用主动对列管理AQM(Active Queue Management)。所谓”主动“就是不要等到路由器的队列长度达到最大值时才不得不丢弃后面到达的分组,这样就太被动了。应当在队列长度达到某个值得警惕得数值时(即网络拥塞有了某些拥塞征兆时),就主动丢弃到达的分组。这样就提醒了发送方放慢发送的速率,因而有可能使网络拥塞的程度减轻,甚至不出现网络拥塞。
实现AQM的方法有随机早期检测RED(Random Early Detection),但是已经不被推荐使用了。
5.9 TCP的运输连接管理 ---- 重要
TCP是面向连接的协议,运输连接是用来传送TCP报文的。TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程。
运输连接分为三个阶段:连接建立、数据传送和连接释放。运输连接的管理就是使运输连接的建立和释放都能正常地进行。
在TCP连接建立过程中要解决以下三个问题:
1、要使每一方能够确知对方的存在;
2、要允许双方协商一些参数(如:最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等等);
3、能够对运输实体资源(如:缓存大小、连接表中的项目等)进行分配。
TCP连接的建立采用客户服务器方式。主动发起连接建立的应用进程叫做客户(client),而被动等待连接建立的应用进程叫做服务器(server)。
5.9.1 TCP的连接建立 ---- 三次握手
TCP建立连接的过程叫做握手,握手需要在客户和服务器之间交换三个TCP报文段。
TCP报文段首部格式:
序号:本报文段所发送的数据的第一个字节的序号;
确认号ack:期待收到对方下一个报文段的第一个数据字节的序号;
确认ACK:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效;
同步SYN:连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段;
若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文;
终止FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接。
最初客户端和服务端都处于CLOSED(关闭)状态。本例中A主动打开连接,B被动打开连接。
一开始,B的TCP服务器进程首先创建传输控制块TCB,准备接受客户端进程的连接请求。然后服务端进程就处于LISTEN(监听)状态,等待客户端的连接请求。如有,立即作出响应。
A的TCP客户端进程也是首先创建传输控制块TCB。然后,在打算建立TCP连接时,向B发出连接请求报文段,这时首部中的同步位SYN=1,同时选择一个初始序号seq=x。TCP规定,SYN报文段(即SYN=1的报文段)不能携带数据,但要消耗掉一个序号。这时,TCP客户进程进入SYN-SENT(同步已发送)状态。
B收到连接请求报文后,如果同意建立连接,则向A发送确认。在确认报文段中应把SYN位和ACK位都置1,确认号是ack = x + 1,同时也为自己选择一个初始序号seq = y。请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。这时TCP服务端进程进入SYN-RCVD(同步收到)状态。
TCP客户进程收到B的确认后,还要向B给出确认。确认报文段的ACK置1,确认号ack = y + 1,而自己的序号seq = x + 1。这时ACK报文段可以携带数据。但如果不携带数据则不消耗序号,这种情况下,下一个数据报文段的序号仍是seq = x + 1。这时,TCP连接已经建立,A进入ESTABLISHED(已建立连接)状态。
为什么是三次握手,而不是两次握手或者四次握手呢?
不可以两次握手的原因:
为了防止已经失效的链接请求报文段突然又传送到了B,因而产生错误。比如下面这种情况:A发出的第一个连接请求报文段并没有丢失,而是在网路结点长时间滞留了,以致于延误到连接释放以后的某个时间段才到达B。本来这是一个早已失效的报文段。但是B收到此失效的链接请求报文段后,就误认为A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。
对于上面这种情况,如果不进行第三次握手,B发出确认后就认为新的运输连接已经建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了。
如果采用了三次握手,由于A实际上并没有发出建立连接请求,所以不会理睬B的确认,也不会向B发送数据。B由于收不到确认,就知道A并没有要求建立连接。
不需要四次握手的原因:
有人可能会说A发出第三次握手的信息后在没有接收到B的请求就已经进入了连接状态,那如果A的这个确认包丢失或者滞留了怎么办?
我们需要明白一点,完全可靠的通信协议是不存在的。在经过三次握手之后,客户端和服务端已经可以确认之前的通信状况,都收到了确认信息。所以即便再增加握手次数也不能保证后面的通信完全可靠,所以是没有必要的。
5.9.2 TCP的连接释放 ----- 四次挥手
数据传输结束后,通信的双方都可以释放连接。现在A和B都处于ESTABLISHED状态。
A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的终止控制位FIN置1,其序号aeq = u(等于前面已传送过的数据的最后一个字节的序号加1),这时A进入FIN-WAIT-1(终止等待1)状态,等待B的确认。请注意:TCP规定,FIN报文段即使不携带数据,也将消耗掉一个序号。
B收到连接释放报文段后立即发出确认,确认号是ack = u + 1,而这个报文段自己的序号是v(等于B前面已经传送过的数据的最后一个字节的序号加1).然后B就进入CLOSE-WAIT(关闭等待)状态。TCP服务端进程这时应通知高层应用进程,因而从A到B这个方向的连接就释放了,这时的TCP连接处于半关闭(half-close)状态,即A已经没有数据要发送了,但B若发送数据,A仍要接收。也就是说,从B到A这个方向的连接并未关闭,这个状态可能会持续一段时间。
A收到来自B的确认后,就进入FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段。
若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使FIN=1。假定B的序号为w(在半关闭状态,B可能又发送了一些数据)。B还必须重复上次已发送过的确认号ack = u + 1。这时B就进入LAST-ACK(最后确认)状态,等待A的确认。
A在收到B的连接释放报文后,必须对此发出确认。在确认报文段中把ACK置1,确认号ack = w + 1,而自己的序号seq = u + 1(前面发送的FIN报文段要消耗一个序号)。然后进入TIME-WAIT(时间等待)状态。请注意,现在TCP连接还没有释放掉。必须经过时间等待计时器设置的时间2MSL(MSL:最长报文段寿命)后,A才能进入到CLOSED状态,然后撤销传输控制块,结束这次TCP连接。当然如果B一收到A的确认就进入CLOSED状态,然后撤销传输控制块。所以在释放连接时,B结束TCP连接的时间要早于A。
为什么A在TIME-WAIT状态必须等待2MSL的时间呢?
1、为了保证A发送的最后一个ACK报文段能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段。接着A重传一次确认,重新启动2MSL计时器。最后,A和B都正常进入到CLOSED状态。如果A在TIME-WAIT状态不等待一段时间,而是在发送完ACK报文段后立即释放连接,那么就无法收到B重传的FIN+ACK报文段,因而也不会再发送一次确认报文段,这样,B就无法按照正常步骤进入CLOSED状态。
2、防止已失效的连接请求报文段出现在本连接中。A在发送完最后一个ACK报文段后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个连接中不会出现这种旧的连接请求报文段。
保活计时器:
除时间等待计时器外,TCP还有一个保活计时器(keepalive timer)。设想这样的场景:客户已主动与服务器建立了TCP连接。但后来客户端的主机突然发生故障。显然,服务器以后就不能再收到客户端发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就需要使用保活计时器了。
服务器每收到一次客户的数据,就重新设置保活计时器,时间的设置通常是两个小时。若两个小时都没有收到客户端的数据,服务端就发送一个探测报文段,以后则每隔75秒钟发送一次。若连续发送10个探测报文段后仍然无客户端的响应,服务端就认为客户端出了故障,接着就关闭这个连接。
TCP的有限状态机: