TCP协议

概念

传输控制协议TCP(Transmission Control Protocol)
TCP协议用于在各种网络上提供有序可靠的面向连接的数据传输服务。TCP数据报长度要求小于64K字节。TCP协议是面向字节流的,为每个字节分配一个序号(IP是面向报文流的)

TCP报头格式

TCP报头格式.png

• 源端口和目的端口:各16位
• 序号和确认号:以字节为单位编号,各32位,保证数据的有序的到达
• TCP每次发送的报文段的首部中的序号字段数值表示该报文段第一个字节的序号
• TCP确认号表示接受端期望下次收到的数据中的第一个数据字节的序号
• TCP头的长度:4位,长度单位为32位字
• 6位的保留域
• 6位的标识位:置1表示有效
• URG:和紧急指针配合使用,发送紧急数据
• ACK:确认号是否有效
• PSH:指示发送方和接收方将数据不做缓存,立刻发送或接收
• RST:由于不可恢复的错误重置连接
• SYN:用于连接建立指示
• FIN:用于连接释放指示
• 窗口大小:用于基于可变滑动窗口的流控,指示发送方从确认号开始可以再发送窗口大小的字节流
• 校验和:为增加可靠性,对TCP头,数据和伪头计算校验和

TCP连接管理

由于两军问题(two-army problem)的存在,可以证明不存在安全的通过N次握手实现对称式连接释放的方法

两军问题.png

但是在实际的通信过程中,使用【三次握手 + 定时器】的方法释放连接在绝大多数情况下是成功的

连接建立过程中要解决以下三个问题:
要使每一方能够确知对方的存在;要允许双方协商一些参数(如最大报文段长度,最大窗口大小,服务质量等);能够对运输实体资源(如缓存大小,连接表中的项目等)进行分配

三次握手建立连接的过程

1.服务器方执行LISTEN和ACCEPT原语,被动监听;客户方执行Connect原语,产生一个SYN为1和ACK为0的TCP段,表示连接请求;
2.服务器方检查是否有服务进程在所请求的端口上监听,若没有,回答RST置位(重新建立传输连接)的TCP段;若有服务进程监听,发出一个SYN置1和ACK置1的TCP段表示连接确认,并请求与对方的连接;
3.发起方收到确认后,发出一个SYN置0和ACK置1的TCP段表示给对方的连接确认

三次握手建立连接的过程.png
为什么是三次握手而不是两次握手?

假设A发出连接请求,但因请求报文丢失而未收到B的确认报文,于是A再重新传一次连接请求。重传的收到了确认,建立了连接。此时A共发了两次连接请求报文段,第一个丢失,第二个到达B,没有"已失效的连接请求报文段"
但如果A发出的第一个连接请求报文段没有丢失,而是在某些网络结点长时间滞留,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的报文段,但B收到此无效的连接请求报文段后,误以为是A又发出一次新的连接请求,于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了。由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的连接已经建立,并一直等待A发来数据。B的许多资源就这样浪费了

四次挥手的连接释放

释放连接时,发出FIN位置1的TCP段并启动定时器,在收到确认后关闭连接。若无确认并且超时,也关闭连接

单向的连接释放.png

当A接收到B的确认,从A到B的连接就释放了,连接处于半关闭状态。即A已经没有数据要发送了。但如果B还发送数据,A仍接收。B发完数据会给A发送释放连接的请求,A收到后给B回复确认,B收到后,A和B连接断开

TCP的有限状态机.png
为什么要四次挥手?

TCP是全双工的通信机制,每个方向必须单独进行关闭

四次挥手断开链接.png
为什么不能用三次握手中捎带应答机制减少一次握手?

TCP是全双工通信的,S收到断开链接请求后只是表示C端不会传输数据到S端了,但是并不表示S端不传输数据到C端。如果采用捎带应答,S端将无法把剩余的数据传输到C端

为何最后一次ACK之后需要等待2MSL(4分钟)的时间?

第一,TCP是可靠协议,必须保证A发送的最后一个ACK报文能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段。如果A在TIME-WAIT状态不等待一段时间,而是在发送完ACK报文段后就立即释放连接,就无法收到B重传的FIN+ACK报文段,因而也不会再发送一次确认报文段。这样,B就无法按照正常的步骤进入CLOSED状态
第二,A在发送完ACK报文段后,再经过2MSL时间,就可以使本连接持续的时间所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求的报文段

为何是2MSL的时间?

2MSL是报文一个往返的最长时间,我们假设A发送了ACK报文后过了一段时间t之后B才收到该ACK,则有 0 < t <= MSL。因为A并不知道它发送出去的ACK要多久对方才能收到,所以A至少要维持MSL时长的TIME_WAIT状态才能保证它的ACK从网络中消失。同时处于LAST_ACK状态的B因为收到了ACK,所以它直接就进入了CLOSED状态,而不会向网络发送任何报文。晃眼一看,A只需要等待1个MSL就够了,但仔细想一下其实1个MSL是不行的,因为在B收到ACK前的一刹那,B可能因为没收到ACK而重传了一个FIN报文,这个FIN报文要从网络中消失最多还需要一个MSL时长,所以A还需要多等一个MSL

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP默认有个定时器,每次收到客户端的请求后会把定时器设置好,通常设置两小时,超过两小时还没收到数据
服务端会发送一个探测报文,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接

TCP传输策略

TCP的窗口管理机制

基于确认和可变窗口大小;窗口大小为0时,正常情况下,发送方不能再发TCP段。但有两个例外:紧急数据可以发送;为防止死锁,发送方可以发送1字节的TCP段,以便让接收方重新声明确认号和窗口大小

如何改进传输层的性能?

策略1:发送方缓存应用程序的数据,等到形成一个比较大的段再发出
策略2:在没有可能进行“捎带”的情况下,接收方延迟发送确认段(捎带:假设A给B发送数据,B在给A发送确认段的时候会捎带着B要给A发送的数据)
策略3:使用Nagle算法
当应用程序每次向传输实体发出一个字节时,传输实体发出每一个字节并缓存所有其后的字节直至收到对第一个字节的确认。适用于数据发送速度快,而网速较慢的情况

策略.png

策略4:使用Clark算法解决傻窗口症状(Silly window syndrome)
傻窗口症状:当应用程序一次从传输层实体读出一个字节时,传输层实体会产生一个一字节的窗口更新段,使得发送方只能发送一个字节
比如A给B发送1个数据(1字节),B成功接收到但还没向上层提交的情况下,这时B的窗口大小为0。如果这1条数据成功向上层提交了,那么B的窗口大小为1。这时B会告诉A,我现在窗口大小为1,你可以给我发1条数据。A就会给B发1条数据,以此类推。因为每个TCP收发的数据都要带20个头,这样网络资源就被浪费了
解决办法:限制收方只有在具备一般的空缓存或更大段长的空缓存时,才产生一个窗口更新段

TCP拥塞控制

出现拥塞的两种情况:快网络小缓存接受者;慢网络大缓存接收者;
导致网络拥塞的两个潜在因素:网络能力;接收能力

TCP处理第一种拥塞的措施(快网络小缓存)

小缓存是指接收方
在连接建立时声明最大可接收段长度;利用可变滑动窗口协议防止出现拥塞

TCP处理第二种拥塞的措施(慢网络大缓存)

发送方维护两个窗口:接收端窗口和拥塞窗口,发送方窗口按两个窗口的最小值发送;拥塞窗口依照慢启动(slow start)算法和拥塞避免(congestion avoidance)算法变化

接收端窗口rwnd

接收端根据其目前的接收缓存大小所许诺的最新的窗口值,是来自接收端的流量控制。接收端将此窗口值放在TCP报文的首部中的窗口字段,传送给发送端

拥塞窗口cwnd

发送端根据自己估计的网络拥塞程度而设置的窗口值,是来自发送端的流量控制

发送窗口的上限值 = Min【rwnd,cwnd】
当rwnd < cwnd时,是接收端的接收能力限制发送窗口的最大值
当cwnd < rwnd时,则是网络的拥塞限制发送窗口的最大值

慢启动算法

• 连接建立时拥塞窗口初始值为该连接允许的最大段长,不超过64K字节
• 发出一个最大段长的TCP段,若正确确认,拥塞窗口变为两个最大段长
• 发出(拥塞窗口÷最大段长)个最大长度的TCP段,若都得到确认,则拥塞窗口加倍(指数级增长)
以64为1,比如第一次发1,第二次发2,第三次发22,第四次发23,以此类推
• 重复上一步,直至发生丢包超时时间,或拥塞窗口大于阈值,这时就会使用↓这个策略

拥塞避免算法

若拥塞窗口大于阈值(这里为16),从此时开始,拥塞窗口线性增长,一个RTT周期增加一个最大段长,直至发生丢包超时时间;当超时时间发生后,阈值设置为当前拥塞窗口大小(这里为24)的一半(即12),拥塞窗口重新设置为一个最大段长;执行慢启动算法

❀这里的阈值16是经过之前传输数据时的网络的情况以及接收方的接收能力为判断的依据

慢启动+拥塞避免算法循环过程1.png

1.发送窗口的上限值=Min【rwnd,cwnd】。假定对于大缓存接收端窗口足够大,发送窗口大小就等于拥塞窗口的数值,拥塞窗口cwnd的初始值为1

慢启动+拥塞避免算法循环过程2.png

2.发送端收到ACK1后,将cwnd增大到2,于是发送端可以接着发送两个报文段,以此类推,发送窗口指数增长

慢启动+拥塞避免算法循环过程3.png

3.当拥塞窗口cwnd增长到慢启动门限值ssthresh时(即当cwnd=16时),就改为执行拥塞避免算法,拥塞窗口按线性规律增长

慢启动+拥塞避免算法循环过程4.png

4.假定拥塞窗口的数值增长到24时,网络出现超时(表明网络拥塞了)

慢启动+拥塞避免算法循环过程5.png

5.更新ssthresh值为12(即原发送窗口的一半),拥塞窗口再重新设置为1,并重新执行慢启动算法

你可能感兴趣的:(TCP协议)