在不可靠的IP层之上实现的可靠的数据传输协议,主要用处是让传输变得可靠,有序,无丢失和不重复。
协议特点
-
TCP是面向连接的传输层协议,TCO连接是一条逻辑链接
-
每条TCP连接只能有两个端点,每条TCP连接只能是端到端的(进程对进程)
-
TCP提供可靠交付的服务,保证传送的数据无差错,不丢失,不重复且有序
-
TCP提供全双工通信,允许通信双方的应用进程在任何时候发送数据,为此TCP连接的两个端点都设有发送缓存和接收缓存,用来临时存放双向通信的数据。
-
TCP是面向字节流的,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但是TCP把应用程序交下来的数据仅视为一连串的无结构的字节流
TCP和UDP的报文长度的决定方式不一样,UDP报文的长度由发送应用进程决定,但是TCP报文的长度是根据接收方给出的接收窗口值和当前网络拥塞情况决定。
TCP报文
TCP传送的数据单元称为报文段(IP传送的数据单元称为分组,数据报)。TCP报文段既可以用来运载数据,又可以用来建立连接、释放连接和应答。一个TCP报文段分为首部和数据两部分,TCP首部长度有20B是固定的,后面是不固定的首部,首部长度是4B的整数倍。
TCP的全部功能体现在其首部的各个字段中,各个字段意义如下:
-
源端口和目的端口:各占2B
-
序号:占4B,范围是0-4,294,967,295(2的32次方-1)。TCP是面向字节流的,一般一个字节对应一个序号。序号字段的值指的是报文段所发送数据的第一个字节的序号
-
确认号:占4B,是期望收到对方下一个报文段的第一个数据字节的序号。若确认号为N,那就表示N-1之前的所有字节都正确收到了。
-
数据偏移(首部长度):占4位,单位为4B
-
保留:占6位,留作今后使用,目前置为全0
-
6个标识位:
-
紧急位URG:当URG=1时,就表示紧急指针字段有效,紧急指针前的所有数据段要尽快传送
-
确认位ACK:只有ACK=1时确认号才生效。TCP规定,在连接建立后的所有TCP报文段都必须把ACK置为1
-
推送位PSH:接收方收到了PSH=1的报文段,就把这个报文尽快地交付给应用进程,而不用等到整个缓存都填满之后再提交
-
复位位RST:RST=1的时候,说明连接出现严重差错,必须先释放连接然后再重新建立连接
-
同步位SYN:SYN=1时,就表示这个报文段是一个连接请求或连接接受报文
-
终止位FIN:用来释放一个连接。当FIN=1的时候,表示这个报文段的发送方的数据已近发送完毕,并要求断开连接
-
-
窗口:占2B,这个字段指出发送方现在允许接收方发送的最大数据量
-
校验和:占2B,用来校验首部和数据的正确性。(UDP中协议字段是17,但是TCP中协议字段是6)
-
紧急指针:占2B,只有当URG=1的时候生效,指针所指位置是紧急数据的最尾端
-
填充:为了使首部长为4B的倍数
TCP连接管理
TCP连接有三个阶段:连接建立,数据传送和连接释放。
TCP连接的建立(三次握手)
-
建立连接前,服务器处于LISTEN状态,等待用户的连接请求
-
客户机的TCP首先向服务器的TCP发送连接请求报文段。报文段的首部的SYN=1,seq(序号)是随便生成的。发送完后客户机进入SYN-SENT状态(消耗一个序号)
-
服务器收到客户机的TCP报文段之后,给这个TCP连接分配缓存和变量,同时回送一个确认报文段,报文段首部的SYN=1,ACK=1,seq(序号)是随便生成的,ack(确认号)=x+1。发送完后服务器进入SYN-RCVD状态
-
客户机收到确认报文段之后,还要向服务器发送一个确认报文段,并为这个TCP连接分配缓存和变量,这个确认报文段可以开始发送数据了(毕竟服务器已近分配了资源),如果这个报文携带数据了,那么就消耗一个序号,如果没有携带数据,那么就不消耗序号
注意,最先分配资源的一定是服务器
TCP连接的释放(四次握手)
-
客户机打算关闭连接的时候,向服务器发送一个FIN=1的连接释放报文段。也就是说,从这个报文段开始,客户机就不再向服务器发送报文段了。但是服务器还是可以向客户机发送报文段的(TCP是一个全双工通信的协议),FIN=1的报文段不论是否携带数据部分,都消耗一个序号,此时客户机进入FIN-WAIT-1状态
-
服务器收到报文之后发出确认,但是服务器不会马上切断连接(毕竟服务器也有要发送的数据,也许这个时候没发玩)。这个时候服务器进入CLOSE-WAIT状态,与此同时客户机进入FIN-WAIT-2状态
-
当服务器发送到最后一个报文段的时候,它就可以让这个报文段的FIN=1,也就是说,服务器向客户机表示服务器这边也发完了所有的数据,从这个报文段开始服务器就不再向客户机发送消息了,这个时候服务器进入LAST-ACK状态
-
客户机收到服务器发送的释放报文段之后,就发送一个确认给服务器,也就是说发送一个ACK=1的报文段给服务器。发送完之后就进入TIME-WAIT状态。但是发送完之后不代表这个连接就结束了,还要等待两个MSL(最长报文段寿命)之后客户机才会进入CLOSE状态。而服务器收到了客户机最后的那一个报文段之后就进入了CLOSE状态
TCP的可靠传输
TCP使用了校验和,序号,确认和重传等机制来让TCP的传输变得可靠这一目的。其中TCP的校验和计算和UDP相同。
序号
TCP首部的序号字段用来确保数据能够有序地提交给应用层。TCP是面向字节流的,也就是说,TCP会给每个字节一个编号,然后几个字节组成一个报文段的数据部分进行传送。报文段首部的序号字段所表示的值是这若干个字节的第一个字节的序号
确认和重传
首先,确认和重传是相绑定的。
TCP的确认用的是累计确认的方法,和数据链路层的确认方法相同,这里不过做赘述。
TCP的重传分两种,一种是超时导致重传,另一种是冗余ACK导致重传。
超时导致重传内有一个RTT的计算的算法,也就是:新估计RTT=(1-α)×旧RTT+α×新RTT样本(也就是新的实际RTT数值)
冗余ACK指的是当一方收到了另一方3个一模一样的ack(确认号)的时候,就认为确认号所指的那一段数据是丢失了的,那么发送方就会立即重传相应的报文段。
TCP流量控制
TCP提供流量控制服务来消除发送方发送速率太快导致的接收方缓存区溢出的可能性。也就是说,我们可以把TCP流量控制理解为一个发送方的发送速度控制服务。
接收方会有个接收窗口,这个接收窗口是用来表示接收方的接收缓存内还剩下多少的空间。而发送方会根据这个接收窗口来改变自己的发送的报文段的数据段的大小。
假设A的缓存为300B,进行流量控制的过程如下图所示
TCP拥塞控制
拥塞控制指的是防止过多的数据注入网络,保证网络中的路由器或链路不至于过载。
拥塞控制同流量控制的区别:拥塞控制是让网络能够承受现有的网络负荷,是一个全局性的过程,涉及所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制是指点对点的通信量的控制,是一个端到端的问题。
TCP会要求发送方维护两个窗口:
-
接收窗口 rwnd,接收方根据目前接收缓存大小所许诺的最新窗口值,反映接收方的容量。由接收方根据其放在TCP报文的首部的窗口字段通知发送方
-
拥塞窗口 cwnd,发送方根据自己估算的网络拥塞程度,而设置的窗口值
一般来说,发送方的发送的数据段的最大值取为min{rwnd,cwnd}
拥塞控制一共有四个算法,两个组合:(慢开始,拥塞避免),(快重传,快恢复)
(以下的所有的控制建立在接收方的缓存够大的情况下,也就是接收方的接受窗口足够大)
慢开始和拥塞控制
最开始的时候,我们令拥塞窗口为1,也就是一个最大报文段长度MSS。每收到一个对新报文的确认后,将cwnd加1。用这样的方式逐步增大发送放的cwnd,可以使分组注入网络的速率更加合理。
使用慢开始算法和拥塞控制算法的判断值是ssthresh,也就是"门限":
-
当swnd<ssthresh的时候,使用慢开始算法
-
当swnd>ssthresh的时候,使用拥塞避免算法
-
当swnd=ssthresh的时候,使用慢开始算法或拥塞避免算法都可以
拥塞避免算法是不论收到几个对报文的确认,一次都只加一个cwnd,而不是像慢开始那样呈现一个指数的爆炸式增长。
每当发现当前的cwnd会导致堵塞之后,我们就把当前cwnd/2作为新的ssthresh,然后将cwnd置为1,重新开始慢开始算法
传输轮次:一个RTT。
快重传和快恢复
如图所示,当发送方接收到3个冗余ACK的时候(此时还没有超过时限,也就是说发送方没有意识到这个时候已经拥塞了),发送方就立即重传ACK所期望的数据,并且把cwnd置为新的ssthresh值,直接执行拥塞避免算法。而不用再次走慢开始从1开始,这样就增加了数据传输的平均数率。