TCP协议
TCP协议即传输控制协议,是一种面向连接的、可靠的、基于字节流的通信协议。为了保证传输的可靠性,TCP协议通过三次握手来建立连接。数据在传输完成后,需要通过四次挥手来关闭连接。
TCP首部格式
当一个连接被建立或者被断开时,交换的只有TCP头部,没有数据。所以我们先来看一下TCP首部的报文格式,如下图:
-
源端口和目的端口:各占2个字节,分别写入源端口和目的端口
-
序号:占4个字节,在一个TCP连接中传送的字节流的每一个字节都按顺序编号,本字段表示本报文段所发送数据的第一个字节的序号。
-
确认号:占4个字节,期望收到对方下一个报文段的第一个数据字节的序号。如果确认号为N,则证明到序号N-1为止的所有数据都已正确收到。
-
数据偏移(首部长度):占4位,表示TCP报文段的数据起始处距离TCP报文段的起始处有多远,以4字节为单位,表示TCP首部的长度
-
保留:占6位,保留今后使用。
-
6个控制位:
- URG:紧急位,置为1后,表明此报文段中有紧急数据,是高优先级的数据,应尽快传送,不用在缓存里排队,配合紧急指针字段使用
- ACK:确认位,ACK=1时确认号才有效,在连接建立后所有传送的报文段都必须把ACK置为1.
- PSH:推送位,PSH=1时,接收方尽快接收交付给应用进程,不再等到缓存填满再向上交付
- RST:复位,RST=1时,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立传输连接
- SYN:同步位,SYN=1时,表示是一个连接请求/接受报文
- FIN:终止位,FIN=1时,表明此报文段发送方数据已发完,要求释放连接
-
窗口:占2字节,也就是0到2的16次方-1之间的整数,指的是发送本报文段的一方的接收窗口,即现在允许对方发送的数据量
-
校验和:占2字节,校验首部和数据
-
紧急指针:只有当URG=1时才有意义,指出本报文段中紧急数据的字节数。
-
选项:长度可变,定义一些其他可选的参数,例如最大报文段长度MSS(每一个TCP报文段中数据字段的最大长度)、窗口扩大、时间戳、选择确认等
-
填充:如果选项字段不是4字节的整数倍,填充一部分0,使TCP的首部成为4字节的整数倍
TCP连接建立
TCP连接的建立采用客户-服务器方式,主动发起连接建立的应用进程叫做客户,而被动等待连接建立的应用进程叫做服务器。
-
客户端发送连接请求报文段,没有应用层数据。
SYN=1,seq=x- SYN=1表示是一个连接请求报文
- seq序号随机产生记为x
-
服务器端为该TCP连接分配缓存和变量,并向客户端返回确认报文段,允许连接,没有应用层数据。
SYN=1,ACK=1,seq=y,ack=x+1- SYN=1表示连接接受报文
- ACK=1表示确认号字段ack发挥作用
- seq序号随机产生记为y
- ack表示已经接收到序号x了,期待客户端接下来发送的序号是从x+1开始
-
客户端为该TCP连接分配缓存和变量,并向服务器端返回确认的确认,可以携带数据。
SYN=0,ACK=1,seq=x+1,ack=y+1- SYN=0,只有在连接请求和连接接受时SYN才等于1
- ACK=1,表示确认号字段ack有效
- ack=y+1,表示已经接收到序号y了,接下来期待服务器发送的序号从y+1开始
- seq=x+1,表示发送的开始序号为x+1,因为之前客户端已经发送过x序号
由于在建立连接过程中客户端和服务器端都会为TCP连接分配缓存和变量,这时候就可能会出现SYN洪泛攻击。
SYN洪泛攻击是指攻击者发送TCP SYN,SYN是TCP三次握手中的第一个数据包,当服务器返回ACK后,该攻击者就不对其进行确认,那这个TCP连接就处于挂起状态,也就是所谓的半连接状态。服务器收不到再确认的话,还会重复发送ACK给攻击者。这样更加浪费服务器的资源。攻击者就对服务器发送大量的这种TCP连接,由于每一个都没法完成三次握手,所以在服务器上,这些TCP连接会因为挂起状态而消耗CPU和内存,最后服务器可能死机,就无法为正常用户提供服务了。
解决方法是使用SYN COOKIE,感兴趣的可以自行了解。
TCP连接释放
参与一条TCP连接的两个进程中的任何一个都可以终止该连接,连接结束后,主机中的资源将被释放。
-
客户端发送连接释放的报文段,停止发送数据,主动关闭TCP连接。
FIN=1,seq=u- FIN=1表示要求释放连接
- seq=u,u表示之前已传输数据的最后一个字节的序号+1
-
服务器端回送一个确认报文段,客户端到服务器这个方向的连接就释放了,此时进入半关闭状态,客户端不会再发送传输的数据,但是可以发送ACK报文段。
ACK=1,seq=v,ack=u+1- ACK=1,表示确认号字段ack有效
- seq=v,v表示服务器端之前已传输数据的最后一个字节的序号+1
- ack=u+1,表示服务器已经接收到序号u了,期待客户端发送的序号从u+1开始
-
客户端接收到第2步服务器给的确认报文后,不用回复,继续等待服务器端发送关闭连接报文,服务器发送完数据之后,向客户端发送连接释放的报文段。
FIN=1,ACK=1,seq=w,ack=u+1- FIN=1,表示要求释放连接
- ACK=1,表示确认号字段ack有效
- seq=w,表示服务器端最后传输数据的最后一个字节的序号+1
- ack=u+1,因为客户端没有再发送数据,所以期待的序号还是u+1开始
-
客户端接收到服务器端的释放连接请求,回复一个确认报文段,再等到时间等待计时器设置的2MSL(最长报文段寿命的2倍)后连接彻底关闭(服务器端没有接收到确认报文段后,服务器会重试发送FIN报文段,所以客户端需要等待2倍的最长报文段寿命时间)。
ACK=1,seq=u+1,ack=w+1- ACK=1,表示确认号字段ack有效
- seq=u+1,表示序号从u+1开始
- ack=w+1,表示已经接收到序号w了,期待服务器发送的序号从w+1开始
服务器接收到客户端你的确认报文后,进入关闭状态。
TCP可靠传输
可靠传输是指:保证接收方进程从缓存区读出的字节流与发送方发出的字节流完全是一样的。
TCP实现可靠传输的方式主要有:
- 校验和:接收方通过加上伪首部计算检验和的方式,来检查数据是否有差错和异常,如果有问题则丢弃这个报文段。
- 序号:在一个TCP连接中传送的字节流的每一个字节都按顺序编号,序号字段表示本报文段所发送数据的第一个字节的序号。接收方会对数据进行排序,将有序的数据传输给应用层,如果有重复的数据就丢弃。
- 确认:发送方在发送完成一个报文段后,不会删除本地的缓存,需要等到接收方回复的确认报文收到后,才会将本地的缓存清除。
- 重传:发送方在规定的时间内没有收到确认就要重传已发送的报文段。
为了防止一些没必要的超时重传,TCP采用自适应算法,动态的改变重传时间RTTS(加权平均往返时间)。
TCP一次性会发送多个报文段,并不会等待收到第一个报文段的确认后再发送第二个,这样等待的时间会比较长。当接收方接收到的报文序号比期望的序号大时,发送一个冗余ACK,指明期待的字节序号。
假设发送1,2,3,4,5报文段。 接收方接收到1,会返回期待下一个序号为2(也就是确认号的位置是2) 接收方接收到3,还是会返回期待下一个序号为2 接收方接收到4,还是会返回期待下一个序号为2 接收方接收到5,还是会返回期待下一个序号为2 发送方接收到3个对于报文段1的冗余ACK后,认为报文段2丢失了,重传2号报文段,这就是快速重传机制。
TCP流量控制
流量控制:让发送方发送数据慢点,要让接收方来得及接收。
TCP利用滑动窗口机制实现流量控制。
在通信窗口中,接收方根据自己接收缓存的大小,动态的调整发送方的发送窗口大小,即接收窗口rwnd(接收方设置确认报文段的窗口字段来将rwnd通知给发送方),发送方的发送窗口取接收窗口rwnd和拥塞窗口cwnd的最小值。
rwnd:滑动窗口,是动态改变的,接收方会根据当前机器的执行效率和缓存上限、当前缓存大小得出一个合适的rwnd值,并且随着ACK回传到发送方。发送方在下次发送数据包的时候,就可以根据新的窗口大小去发送数据了。
当接收方将rwnd设置为0回复给发送方(不允许发送方再发送),先处理本地的缓存,等到处理完成后,然后再向发送方发送rwnd的窗口大小,如果这个消息丢失了,那么接收方会一直等待发送方发送数据,发送方会一直等待接收方是否可以发送的消息,就会出现双方都在等待的问题。
为了解决这个问题,TCP为每个连接都设置一个持续计时器,只要TCP连接的一方收到对方的0窗口通知,就启动持续计时器。如果时间到期了,发送方就会发送一个零窗口的探测报文段,接收方收到后会给出现在的窗口值,若窗口仍然是0,那么发送方就重新设置持续计时器。
TCP拥塞控制
网络资源出现拥塞条件:对资源需求的总和>可用资源。
拥塞控制主要是为了防止过多的数据注入到网络中。
拥塞控制的四种算法:
- 慢开始
- 拥塞避免
- 快重传
- 快恢复
接收窗口:接收方根据接收缓存设置的值。并告知给发送方,反映接收方容量。 拥塞窗口:发送方根据自己估算的网络拥塞程度而设置的窗口值,反映网络当前容量。 发送窗口 = Min(接收窗口rwnd,拥塞窗口cwnd)
在了解慢开始和拥塞避免算法之前,先了解几个名词:
- MSS:最大报文段长度,TCP双方发送的报文段中,包含的数据部分的最大字节数。
- cwnd:也就是拥塞窗口,这个上面已经介绍过了。
- RTT:往返时间,发送方发送一个报文,到接收这个报文的确认报文所经历的时间。
- ssthresh:慢启动阈值,慢启动阶段,若cwnd的大小达到这个值,将转换到拥塞避免模式。
以下图来说明:
纵坐标表示拥塞窗口的大小,默认从1开始,这里的1指的是1MSS。 横坐标表示传输轮次,一个传输轮次代表TCP一次性将窗口内的所有报文发出,所有报文都到达并被确认的时间。也就是一个往返时延RTT。
慢开始指的是建立TCP连接后,采用的第一个调整发送速率的算法。在这个阶段,cwnd通常被初始化为1MSS,慢开始的目的就是尽快找到上限。在慢启动阶段,发送方每接收到一个确认报文,就会翻倍式的增加cwnd。
如图所示:
- 第一次发送窗口cwnd初始为1,在收到确认后,cwnd增长为2
- 第二次发送窗口cwnd为2,在收到确认后,cwnd增长为4
- 第三次发送窗口cwnd为4,在收到确认后,cwnd增长为8
- 第四次发送窗口cwnd为8,在收到确认后,cwnd增长为16.
当cwnd的值大小达到sshtresh的值,则进入拥塞避免模式。
拥塞避免是随着轮次的增加,拥塞窗口缓慢增加,尝试找到一个上限值,假设cwnd增长到24时,网络出现拥塞,那么立即将cwnd重新设置为1MSS,并且将慢启动阈值设置为24的一半,也就是12,重新慢开始。
快重传是指出现网络拥塞时,发送方收到3个重复的冗余确认,说明报文丢失,需要执行快重传算法。
快恢复是指在执行完快重传后,将慢启动阈值降为一半,直接从新的阈值开始拥塞避免,不用再将到1,重新慢开始启动。