可靠连接,TCP协议全解析

Posted 爱奇志

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可靠连接,TCP协议全解析相关的知识,希望对你有一定的参考价值。

一、前言

TCP是一种传输层协议,全称为Transmission Control Protocol,中文名传输控制协议,主要包含以下特点:

(1)TCP是建立在不可靠的IP协议上的面向连接的可靠的传输层协议。

我的理解:

IP协议是不可靠的:IP协议不保证数据报能成功到达目的地,它是尽最大努力的交付,路由器对IP报错误处理方式是丢包,并发送ICMP给源地址,所以IP协议是不可靠的。

TCP协议是可靠的:TCP协议传送前通过三次握手建立连接,实现可靠传输。

TCP协议是面向连接的:TCP会在Client与Server之间建立并维护一个连接,并且在通信过程中监视连接的状态,所以TCP是面向连接的。

又因为IP协议是网络层协议,TCP协议是传输层协议,整个TCP协议是IP协议的数据部分,所以说TCP协议是建立在IP协议之上的。

(2)每一条TCP协议只有两个端点(Client端与Server端),而且每一条TCP协议只能是点对点的。

(3)TCP提供可靠的数据交付,保证传送的数据无差错、不丢失、不重复且有序。

保证这四点,关键在于Seq(序号字段)和Ack(确认号字段),序号字段和确认号字段都是TCP的首部字段,均占四个字节。

无差错:因为网络时延和校验和字段错误,解决方法为快速重传,保证无差错。

不丢失:因为网络拥塞致使超时未收到确认,解决方法为超时重传,保证不丢失。

不重复:因为IP包到达的无序致使上层TCP报文段的紊乱,解决方法是接收端根据Seq字段按顺序排列,保证不重复。

有序:因为IP包到达的无序致使上层TCP报文段的无序,解决方法是接收端根据Seq字段按顺序排列,保证有序。

(4)TCP协议是全双工通信,即通信双方可以同时发送和接收信息,发送端和接收端同时设有发送缓存和接收缓存,用来存放临时通信数据。

发送缓存中两种数据:准备发送尚未发送的数据、已发送未被确认的数据;

接收缓存中两种数据:不按序达到的数据、按序达到但尚未被接收的数据。

附:从通信双方的交互方式来看,可以分为以下三种类型:

单向通信(单工通信):只能有一个方向的通信而不能有反方向的交互。

双向交替通信(半双工通信):通信双方都可以发送和接收信息,但是不能同时收发,必须交替进行。

双向同时通信(全双工通信):通信双方可以同时发送和接收信息。

(5)TCP协议是面向字节流的,TCP将应用层(即应用程序)交付下来的数据看成一连串无结构的字节流。

对比学习——UDP是面向报文的,TCP是面向字节流的

UDP是面向报文的,发送方的UDP对应用层交下来的报文,不合并,不拆分,只是在其上面加上首部后就交给了下面的网络层,也就是说无论应用交给UDP多长的报文,它统统发送,一次发送一个。而对接收方,接到后直接去除首部,交给上面的应用层就完成任务了。因此,它需要应用层控制报文的大小。

TCP是面向字节流的,它把上面应用层交下来的数据看成无结构的字节流来发送,发送方TCP会将数据放入发送缓存中,等到可以发送的时候就发送,不能发送就等着,TCP会根据当前网络的拥塞状态来确定每个报文段的大小。

二、TCP报文结构

TCP协议数据单元称为报文段。(PS:UDP协议数据单元称为数据报)

对比学习——各层协议数据单元名称如下:

物理层协议数据单元名称为比特;

数据链接层协议数据单元名称为帧(或数据帧);(如SDLC协议、HDLC协议、PPP协议、STP协议、帧中继协议)

网络层协议数据单元名称为分组(或包、数据包);(如IP协议、RIP协议、OSPF协议)

传输层协议数据单元名称为报文段(或段、数据报);(如TCP协议数据单元称为报文段、UDP协议数据单元称为数据报)

应用层协议数据单元名称为报文。(如HTTP协议、FTP协议)

一个TCP报文段TCP头部和TCP数据部分,整个TCP报文段封装在IP数据部分,如图:

TCP首部20个字节是固定的,如下(图中包括最后的选项和填充):

TCP首部长度为20字节~60字节,TCP首部最短为20字节,可以使用填充字段填充,但是无论如何TCP首部长度为4个字节的整数倍,故长度取值为20字节 24字节 28字节 32字节 36字节 40字节 44字节 48字节 52字节 56字节 60字节。

各个字段意义(按排列顺序介绍):

源端口和目的端口:各占2个字节,一共占4个字节。功能:端口范围为0~65535,该端口是传输层与应用层的服务端口,传输层的复用和分用都要通过端口来实现。

序号字段:占4个字节。功能:TCP是面向字节流的(TCP传送是一个一个字节来传送的),所以TCP传送的时候每一个字节都编上一个序号,发送报文段的时候,整个报文段的序号(即这个序号字段)的值为该报文段第一个字节的序号。

确认号字段:占4个字节。功能:是期待接收方下一个报文段的序号(即下一个报文段的第一个字节的序号)。若确认号为N,则表示期待下一个报文段发送序号为N开始的字节流过来(注意:因为TCP是有序的,所以实际上现在0~N-1都已经接收到,所以期待发送N开始的字节流过来)。

数据偏移字段:占4位(1个字节)。功能:该字段又称为首部长度字段,记录的是TCP报文段起始处与TCP报文段数据部分起始处的距离,即(这个距离就是)当前TCP报文段首部长度。该字段占4位,理论上值范围为0-15,实际值范围5-15,表示TCP首部长度为20字节~60字节。

数据偏移字段记录TCP首部长度时,长度单位是4字节,值为5,表示TCP首部长度为45=20字节,值为15,表示TCP首部长度为155=60字节。

保留字段:占6位。功能:在设计TCP首部时,保留为以后所用,但直到现在仍未用到,置为0.

URG紧急位:占1位。功能:占1位,只有0和1两个值。URG=1表示紧急指针有效,表示该报文段中有紧急数据,应优先传送该报文段。注意,URG需要和紧急指针配套使用,URG紧急位只是指示是否紧急,紧急指针指示实际的紧急数据(实际紧急数据是从第一个字节到紧急指针所指字节)。此外,URG=0表示紧急指针无效,一般处理即可,无需优先传送该报文段。

ACK确认位:占1位。功能:占1位,只有0和1两个值。ACK=1表示确认号字段有效,ACK=0表示确认号字段无效。

TCP规定,连接建立后所有报文段ACK确认位置为1。所以,可以通过ACK=0/1来判断当前是否已建立起TCP连接。

PSH推送位:占1位。功能:占1位,只有0和1两个值。PSH=1表示某端接收到TCP报文段之后,应尽快交付给应用层,而不是等到接收缓存满了后再向上交付;PSH=0表示一般处理,接收缓存满了再向上交付。

RST复位位:占1位。功能:占1位,只有0和1两个值。RST=1表示TCP连接中出现严重差错,必须释放当前连接,然后重新建立起连接。RST=0表示一般处理,继续当前传输。

SYN同步位:占1位。功能:占1位,只有0和1两个值。SYN=1表示这是一个连接请求或连接接收报文。

当SYN=1,ACK=0时,表示当前尚未建立起连接,故这个一个连接请求报文;

当SYN=1,ACK=1时,表示当前已建立起连接,故这个一个连接接收报文。

两者关系:某端发送连接请求报文,SYN=1,ACK=0,为第一次握手,

若对方同意建立连接,则发送响应报文(连接接收报文),SYN=1,ACK=1,为第二次握手。

FIN终止位:占1位。功能:占1位,只有0和1两个值。FIN=1表示此报文段的发送方的数据已发送完毕,并要求释放传输连接。

窗口字段:占2个字节。功能:根据自己接收缓存(即接收窗口)剩余空间指示另一端可以发送的数据量,单位为字节。如:确认号为301,窗口字段为1000,则表示,从301号开始,发送此报文段的一端告诉另一端自己还可以接收1000个字节数据,且期待从301开始,即期待字节序号为301-1300。

检验和:占2个字节。功能:TCP的检验同时检验首部和数据部分,在报文段前面加12个字节伪首部,用于检验之用。

对比学习——关于IP、IMCP、TCP和UDP的检验:

IP校验和只校验20字节的IP报头;而ICMP校验和覆盖整个报文(ICMP报头+ICMP数据);UDP和TCP校验和不仅覆盖整个报文,而且还有12字节的IP伪首部,包括源IP地址(4字节)、目的IP地址(4字节)、协议(2字节,第一字节补0)和TCP/UDP包长(2字节,包含协议头和数据)。

紧急指针:占2个字节。功能:指示出本报文段的紧急数据的范围,即紧急数据有多少个字节。

紧急数据放在TCP报文段数据部分的最前面,紧急指针为16位,范围为0-65535,可以表示前0-66535个字节是紧急数据。

选项字段:长度可变。功能:TCP最初只规定了一种选项,即最大报文段长度MSS,MSS表示TCP报文段中数据字段的最大长度。

填充字段:长度可变。功能:无意义,纯粹填充使TCP首部为4N个字节。

注意:有的读者容易搞错,不是说填充字段自身为4N个字节,而是填充字段将TCP首部填充为4N个字节。如上图所示,TCP首部=20个固定字节+选项字段(3个字节)+填充字段(1个字节),故整个TCP首部24个字节,符合4N个字节。还可以,TCP首部=20个字节+选项字段(5个字节)+填充字段(3个字节),故整个TCP首部28个字节,符合4N个字节。

三、TCP连接(重点:三次握手和四次挥手)

TCP是面向连接的协议,每一个TCP连接都会有三个阶段:连接建立、数据传输、连接释放。

TCP把连接作为最基本的抽象,每一个连接有且仅有两个端点,这两个端点的对象不是主机、不是主机IP地址,不用应用进程,不是协议端口,而是套接字。套接字=IP地址+端口号。

每一条TCP连接都被通信的两个端口(即两个套接字)唯一确定。

TCP连接是采用客户端/服务器方式。主动发起连接建立的一方叫客户端,被动等待连接建立的一方叫服务端。

连接建立时,先启动服务端,启动后等待客户端来连接它,然后再启动客户端,根据套接字(IP地址+端口号)连接服务端,开始三次握手。

连接释放时,先由客户端发起连接释放请求,四次挥手后,连接被释放,然后关闭服务端和客户端。

金手指:连接建立时,是先开启服务端,再开启客户端;连接释放时,四次挥手后,客户端与服务端关闭顺序无所谓。

3.1 三次握手建立连接

第一次握手:客户端发出连接请求报文,当前尚未建立连接,所以ACK确认位为0;seq序号字段为x,表示该报文第一个字节序号为x;SYN同步位为1,则该报文段不携带任何数据。

注意:TCP规定,SYN同步位为1,报文段不携带任何数据。所以,第一次握手和第二次握手不携带任何数据。

值得注意的是,连接请求报文(即第一次握手)不携带数据,但是会消耗掉一个序号,这里消耗序号x。

第二次握手:服务端发出响应请求报文(又称确认报文),当前已建立连接,所以ACK确认位为1;seq序号字段为y,表示该报文第一个字节序号为y;ack确认字段为x+1,表示已成功接收到序号为x+1之前的报文(即成功接收到第一次握手的x报文),下一个期待序号为x+1的报文;SYN同步位为1,则该报文段不携带任何数据。

值得注意的是,确认报文(即第二次握手)仍不携带数据,但是会消耗掉一个序号,这里消耗序号y。

第三次握手:客户端收到服务端发来的确认报文后,还要向服务端给出确认,

值得注意的是,客户端发出的确认报文(即第三次握手)可以携带数据,如携带数据则消耗一个序号,如不携带数据则无需消耗一个序号。

三次握手之后,稳定的TCP连接建立起来,可以向应用层传送数据了。

对于上图的解释:

第一次,客户端发送一个数据报x给服务端,表示自己请求与服务端通信。

但是客户端怎么知道服务端收到了自己发送的数据报文呢?所以服务端必须要有一个响应报文。

所以第二次,服务端发送给数据报y给客户端,告诉它自己已经收到数据报x(通知当前报文的确认字段x+1,表示自己收到了x报文,期待x+1报文),但是服务端怎么知道客户端确实收到了自己的报文y呢?

所以有了第三次握手,客户端发送数据报x+1给服务端,告诉它自己已经收到数据报y(通过当前报文确认字段y+1,表示自己收到y报文,期待y+1报文)。

所以,每一个请求建立要消耗三个报文,然后才能开始数据传送。

注意,服务器资源是在第二次握手时分配的,客户端资源是在第三次握手时分配的。这就使得服务器易到SYN洪泛功能。

补充:什么是SYN洪泛攻击?

在第二次握手完成之后,第三次握手完成之前(即Server发送SYN-ACK之后,收到Client的ACK之前),此时的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。

SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:
#netstat -nap | grep SYN_RECV

问题1:只进行两次握手可以建立可靠连接吗?

回答1:不行,因为特殊情况下会出现问题:假设只使用了两次握手就认为建立了连接,在某种情况下,客户端发出的连接建立报文段出现网路延迟,然后服务端迟迟收不到客户端的这个报文段,所以不会发出确认报文段,而此时客户端因为迟迟没有收到确认报文段,则采用重新发送连接建立报文段,这一次报文段正常传送未超时,两次握手建立连接,客户端和服务端很愉快的传输完数据,然后断开连接。在这之后,最开始的那个连接请求报文段传送到了服务端,服务端认为客户端又发起了一次TCP连接请求,则发出确认报文段,两次握手后认为建立的连接,但是客户端不会给服务端传送数据,造成服务端长期等待。

小结1:两次握手的第一个问题,已失效的连接请求报文段又传送到服务端,造成错误。

解决1:采用三次握手建立连接,刚才那种情况,客户端在自己发出的连接建立报文段延迟后,迟迟没有收到确认报文段,采用重传,重传成功后和服务端三次握手建立连接,愉快地传送完数据后断开连接。在这之后,服务端再收到最开始的延迟的网络请求,给出确认报文,但是客户端不会理睬这个确认报文,所以不会发出第三次握手,服务端等不到第三次握手到来,因未完成三次握手的建立连接,则服务端断开连接,不会盲等。

实例解释:以打电话为例 (两次握手建立连接)

小明给妈妈打电话
小明:喂,妈妈 听得到吗?(网络延迟未传送到)
小明重传:喂,妈妈 听得到吗?(正常传送)
妈妈:嗯,我听到你说话了,你能听到我么? (两次我握手后认为建立了可靠连接)
谈话中…
挂电话。
喂,妈妈 听得到吗?(送到)
妈妈:嗯,我听到你说话了,你能听到我么? (两次我握手后认为建立了可靠连接)
此时小明根本不在电话前,妈妈一直在等。

解决办法:以打电话为例 (三次握手建立连接)

小明给妈妈打电话
小明:喂,妈妈 听得到吗?(网络延迟未传送到)
小明重传:喂,妈妈 听得到吗?(正常传送)
妈妈:嗯,我听到你说话了,你能听到我么?
小明:我能听到你。 (三次我握手后认为建立了可靠连接)
谈话中…
挂电话。
喂,妈妈 听得到吗?(送到)
妈妈:嗯,我听到你说话了,你能听到我么?
连接尚未建立成功,妈妈认为小明不再电话旁,不会再等。

3.2 四次挥手释放连接

客户端或服务器均可主动发起挥手动作,在Java socket编程中,任何一方执行close()操作即可产生挥手操作。这里为了和下面wireshark演示相对应,所以使用的是服务端主动请求释放连接。

第一次挥手:服务端打算关闭连接,就向客户端发送了一条连接释放请求报文段,该报文段与其他报文段不同区别在于FIN终止位为1,表示服务端要释放连接,该报文段不管是否携带数据,均要消耗一个序号,这里消耗序号u.

第二次挥手:客户端接收到服务端发过来的FIN报文段(即连接释放报文段)后给出确认报文,此时,从服务器到客户端的方向的连接就释放了。但是由于TCP是全双工通信,所以客户端到服务端的连接还没有释放,此时客户端发送的数据服务端仍然可以接收,所以下面有一个“数据传送”的方框,表示当前客户端发送的数据服务端还是可以接收。

第三次挥手:客户端继续向服务端发送数据,就是中间那个“数据传送”的方框,当客户端的数据发完了之后,由客户端发起连接释放报文段,该报文段与其他报文段不同在于FIN终止位为1,表示客户端要释放连接。

第四次挥手:服务端接收到客户端发来的FIN报文段(即连接释放报文段)后给出确认报文,至此,整个挥手过程结束。

对于上图的解释:

第一次,服务端发送结束报文(FIN终止位=1)给客户端, 告诉客户端当前报文是自己的最后一个报文,即自己的数据发送完毕。但是服务端怎么知道客户端确实收到了自己的最后一个报文,确实知悉了自己的数据传送完毕了呢?

所以第二次,客户端发送一个报文给服务端,该报文确认字段为x+1,则表示客户端确实收到了报文x,同时告诉服务端,自己还有数据没有传送完毕,告诉服务端不能现在立刻关闭网络通信,需要再等一等。

所以,下面的数据传送都是客户端到服务端,因为服务端已经发送完最后一个报文了(即结束报文FIN终止位=1)。

然后第三次,客户端发送一个终止报文FIN=1给服务端,告诉服务端自己的报文已经发送完毕,但是客户端怎么知道服务端确实收到了自己的最后一个报文呢?

所以第四次,服务端发送一个响应报文ACK确认位=1,确认字段=w+1,表示自己确实收到了报文x。

所以,TCP释放连接,需要消耗四个报文。

问题1:为什么连接建立时三次握手,但是连接释放却是四次挥手?次数不同?

回答1:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

以打电话为例(小明给妈妈打电话)
小明:喂,妈妈听得到么(SYN=1 不携带信息)?
妈妈:嗯,我听到你说话了(ACK=1 确认位),你能听到我么(SYN=1 不携带信息)?
小明:我能听到你,妈妈你午餐吃什么。(ACK=1 确认位 SYN=0 可以携带信息)
谈话中。。。。。
小明:喂,妈妈 我要挂电话了(FIN=1 终止位) 你还有什么要补充的吗?
妈妈:嗯。(ACK=1 确认位 收到连接释放请求)
妈妈:最后补充一点,天气冷了,要多穿点衣服哦(最后一波信息),挂电话了哦(FIN=1 终止位)。
小明:好的,记住了(ACK=1 确认位)。

小结:可以看到连接建立的时候妈妈要发出ACK=1和SYN=1,连接释放的时候妈妈要发出ACK=1和FIN=1,然而连接建立的时候妈妈在一个报文中可以同时发出两个标志ACK=1(确认听到小明说话),SYN=1(询问小明是否听到自己说话);但是连接释放的时候,小明发出连接释放报文段,所以小明的信息一定是发送完毕了,但是妈妈的信息不一定全部发送完毕,所以妈妈先发出ACK=1 (确认收到小明连接释放报文段),然后在发完最后一波信息,在发出FIN=1 终止位,ACK=1与FIN=1需要分两步发出,中间夹着最后一波信息。

所以,连接建立三次握手,连接释放四次挥手。

3.3 wireshark演示

第一步,找到一个网站测试,这里找csdn首页,命令行ping www.csdn.net,取到一个IP地址。

第二步,启动wireshark,准备抓包,使用ip.addr=39.96.252.213,筛选掉一些;然后浏览器中访问www.csdn.net;再回到wireshark,我们抓到了很多包,快速定位到第一次握手,技巧,在Info下面寻找[SYN] Seq=0的,很快就可以找到,演示截图:

第三步,浏览器中关闭www.csdn.net网页,一段时间后,wireshark很快可以抓到四次挥手过程,快速定位到第一次挥手,技巧:Info下面寻找[FIN,ACK],很快就可以找到,截图示意:

四、TCP可靠连接

TCP保证可靠连接的四项机制——校验、序号、确认和重传。(校验即首部结构时候的校验字段,校验首部+数据部分)

因为IP是尽最大努力交付,不能保证可靠,所有TCP需要保证可靠,所谓的可靠传输就是要保证接收方进程从缓存区读出的字节流和发送方发出的字节流是完全一样的。

4.1 序号

TCP的序号是建立在传送的字节流之上的,不是传送的报文段之上的(报文段也有序号,存于序号字段中,其值是数据部分第一个字节的序号)。如下:

第1个报文段第一个字节序号为0,则第1个报文段序号字段值为0;

第2个报文段第一个字节序号为3,则第2个报文段序号字段值为3,其他两个依次可知。

4.2 确认

TCP首部有一个确认字段,该字段实现TCP传输中的确认功能,该字段两个作用:

第一,表示确认字段之前序号的字节均已被成功传送;

第二,表示期待着的下一个报文段的序号,即下一个报文段的数据部分的第一个字节的序号。

TCP默认采用累计确认,即TCP只确认数据流中至第一个丢失字节为止的字节。如下:

如果当前接收端接收到第1个报文段和第3个报文段,其中第2个报文段没有接收到,那么服务端给客户端的返回报文段中,确认字段应为3,表示期待第一个字节序号为3的报文,TCP只数据流中第一个丢失字节为止的字节。

4.3 重传

TCP发送端重传报文段有两种原因,超时或冗余。

4.3.1 超时重传

TCP每发送一个报文段,就会对这个报文段设置一个计时器,如果计时器设置的重传时间到期还没有收到确认,就会进行重传,这种重传是超时重传。

问题:这个重传时间是如何计算出来的?

回答:

第一步:先计算往返时间RTT(基于旧的RTT和新的样本)

第一个测量RTT时,RTTs为测量的RTT值;从第二次开始,新的RTTs=(1-α)旧的RTTs+α新的RTT样本

注意:α推荐值为0.125.

第二步:基于往返时间设置重传时间RTO

给出公式,

RTO=RTTs+4*RTTd

注意:RTTd是RTT的偏差的平均加权值,它与RTTs和新的RTT样本之差有关。当第一次测量时,RTTd取为测量到的RTT样本值的一半,以后测量中,公式为:

 新的RTTd=(1-β)*旧的RTTd+ β* |RTTs- 新的RTT样本|

注意:β推荐值为0.25.

4.3.2 冗余确认(快速重传)

考虑一种情况,发送端发送了序号为1、2、3、4、5的5个TCP报文段,其中2号报文段因为某种原因丢失,无法到达接收方,只有1、3、4、5到达接收方,接收方接收到序号为1的报文段,下一个期待着的是序号为2的报文段,但是它没有收到,反而是收到了序号为3、4、5的报文段,但是它不需要(因为TCP是有序的,接收端要保证有序性,没有收到序号为2的报文段,不会接收后面序号的报文段)。TCP规定当比自己期望序号大的失序报文段到达接收端时,发一个冗余ACK,指名自己期望的下一个字节序号。所以此时接收端不会接收3、4、5序号报文段,而是会发送一个冗余ACK给发送端。TCP又规定,发送端对同一个报文段收到3个冗余ACK时,就认为这个报文段丢失,采取重传该报文段。

小结——冗余确认得以实现的两个底层原理:

第一,从接收端来看,TCP规定当比自己期望序号大的失序报文段到达接收端时,发一个冗余ACK,指名自己期望的下一个字节序号。

第二,从发送端来看,TCP规定发送端对同一个报文段收到3个冗余ACK时,就认为这个报文段丢失,采取重传该报文段。

试想一下,如果没有这种冗余确认机制,那么发送端就一定要使用第一种重传方式,一定要等到超过重传时间而没有收到序号2的报文段的确认报文之后,进行超时重传,这样被动等待来触发超时重传一定会等很久,而是用主动冗余确认机制,则更加快捷,所以这个因冗余确认而触发的重传叫快速重传。

五、TCP流量控制

TCP传输中,全双工通信,两端都可以发送和接收,两端都有个一个缓冲窗口,当前处于发送的那一方的缓冲窗口称为发送窗口,当前处于接收的那一方的缓冲窗口称为接收窗口。

TCP流量控制就是接收端的接收窗口根据自己还可以接收的数据量,设置首部结构中的窗口字段,将报文发送给发送端,发送端根据该报文段的窗口字段设置自己的发送窗口大小,从而达到避免发送方发送数据量过大使接收方缓存溢出。

图解:

由上图,可以看出,接收端B完全可以在确认报文设置窗口字段,来限制发送端的数据传送,从而保证自己的接收缓存不会溢出,这就是TCP的流量控制。

六、TCP拥塞控制

6.1 拥塞控制和流量控制、发送窗口和接收窗口和拥塞窗口

相同点:都是控制发送端的发送窗口,都是对发送端数据传送进行控制。

不同点:流量控制是由接收端根据其还能接收的数据量的大小发出的,目的是为了避免接收窗口的溢出;拥塞控制是由整个网络所有的主机和路由器根据现在的网络负荷情况发出的,目的是为了避免整个网络瘫痪。

关联关系:发送窗口应该同时受到接收窗口和拥塞窗口的控制,取两者的最小值(即较小值),即

发送窗口的上限值=Min[接收窗口rwnd,拥塞窗口cwnd] 

对于该公式的理解是,发送窗口理论上是受到接收窗口rwnd和拥塞窗口cwnd的同时限制,发送窗口受到接收窗口rwnd的限制表示的是TCP流量控制,发送窗口受到拥塞窗口cwnd的限制表示的是TCP拥塞控制,同时限制则是既完成流量控制,又完成拥塞控制。则该公式把TCP的流量控制和拥塞控制统一起来了。

附:虽然理论上是像上面那样说,但是实际传输中,接收方总是有足够大的缓存空间,因而实际传输中,发送窗口大小主要是由网络的拥塞程度来决定的,主要受到拥塞窗口cwnd的限制,即可以将发送窗口等同于拥塞窗口。

接收窗口rwnd是通过当前可以接收的数据量设置TCP首部窗口字段,然后通过确认报文段通知发送端;而拥塞窗口cwnd则是通过慢开始、拥塞避免、快恢复等算法计算出当前拥塞窗口的大小,然后通过全网所有主机、路由器通知发送端的。

6.2 慢开始和拥塞避免

6.2.1 慢开始

TCP建立连接后,开始数据传输,开始发送TCP报文段时,先令拥塞窗口cwnd=1(下图中t=0时,拥塞窗口cwnd=1),单位为一个最大报文长度MSS(即TCP首部选项字段指示的MSS),即拥塞窗口每次加1,就是增加一个MSS,拥塞窗口每次翻倍,就是添加一倍MSS。

慢开始算法是指,每一个往返延迟RTT,拥塞窗口cwnd就会翻倍,但是这样的翻倍无限的,当cwnd的值达到ssthresh阈值,改用拥塞避免算法。

6.2.2 拥塞避免

拥塞避免算法=加法增大+乘法减小

加法增大:每一个RRT使拥塞窗口cwnd加1

乘法减小:当出现一次网络阻塞(即超时),ssthresh阈值变为原来的一半,即乘法减小是ssthresh阈值减小50%。

从拥塞避免算法的表述来看,拥塞避免算法并不能避免拥塞,每一个RRT使拥塞窗口cwnd加一(加法增大),只是相对于慢开始算法的翻倍速度变慢了而已,使拥塞延后了而已。

6.2.3 慢开始和拥塞避免实现图

实际使用中,我们策略是慢开始算法和拥塞避免算法一起使用:

当cwnd<ssthresh时,使用慢开始算法;

当cwnd=ssthresh时,使用慢开始算法或拥塞避免算法;

当cwnd>ssthresh时,使用拥塞避免算法。

注意:下图ssthresh初始值为12.

该图中,第一阶段,t从0到4,cwnd<ssthresh慢开始算法;t=4,cwnd=ssthresh慢开始算法或拥塞避免算法;t从4到12,cwnd>ssthresh拥塞避免算法;t=12,出现超时,网络阻塞,ssthresh变为cwnd一半,24/2=12,cwnd变为1;

第二阶段,t从13到17,cwnd<ssthresh,又是慢开始算法;t=17,cwnd=ssthresh,慢开始或拥塞避免算法;t从17到22cwnd>ssthresh又是拥塞避免算法。

注意:慢开始阶段,若下一个cwnd大于ssthresh,即2当前cwnd>ssthresh,下一个cwnd应该为ssthresh,而不是2当前cwnd,从慢开始算法到拥塞避免算法,cwnd必须在某个时刻等于ssthresh,不能越过ssthresh。上图t=17,cwnd为12而不是16正好说明了这一点。

6.3 快重传和快恢复

6.3.1 快重传

在“四、TCP可靠传输——4.3重传”中,其中有一种冗余确认,就是快速重传,即接收端接收到比自己预期序号大的报文,会发送一个冗余ACK,发送端同时收到某个报文段的三个冗余ACK,会快速重传此报文段,而不用等到重传时间到了未收到确认报文而进行超时重传,提供的这种快速重传的方式是给定情况的一个特殊重传方式。

实际上,在TCP拥塞控制方面,也支持使用这种方式,这就是快重传。快重传:当发送端收到连续三个冗余ACK(即重复确认)时,立刻重传此报文段。

6.3.2 快恢复

快重传和快恢复两者的触发条件相同,都是连续收到3个冗余ACK,但是执行不同。且看下面:

快重启与快恢复区别:

快重传:当发送端收到连续三个冗余ACK(即重复确认)时,立刻重传此报文段。

快恢复:当发送端收到连续三个冗余ACK(即重复确认)时,执行“乘法减小”算法,ssthresh变为当前cwnd的一半,cwnd变为修改后的ssthresh的值(即cwnd也变成当前发送端cwnd的一半)。

七、尾声

可靠连接,TCP协议全解析,完成了。

天天打码,天天进步!!

以上是关于可靠连接,TCP协议全解析的主要内容,如果未能解决你的问题,请参考以下文章

可靠连接,TCP协议全解析

TCP协议解析

TCP协议解析及相关问题

TCP/IP协议全解析 三次握手与四次挥手[转]

网络协议原理解析

TCP协议53