【详解】TCP

TCP是传输层协议,提供可靠的字节流服务,它将大块的数据分割为一个个报文段来管理。

1.TCP的报文段结构

图片转自 如故

在这里插入图片描述

目前我们只需要知道TCP报文段有如下内容:

源端口号和目的端口号:这个不用说,表明该TCP报文来自哪,要去哪。

序号和确认号:这是报文段首部最重要两个字段,也是TCP可靠数据传输的关键部分,下面详细讲。

6比特的标志字段:ACK用于确认是否收到,RST、SYN、FIN用于连接的简历和拆除。PSH和URG暂时不需要了解。

16位窗口大小:该字段用于流量控制,也就是指示接收方愿意接受的字节数量,用来控制发送方的发送速率。

数据:字面意思,传送的具体数据。

序号和确认号

前面提到,TCP是提供可靠的字节流服务,它将数据看成有序的字节流,而一个报文段的序号就是该报文段首字节的字节流编号。举例来说,A向B传送一个5000字节的数据,假设TCP将其每个字节编号,并分成五个报文段,那么第一个报文段的序号就是0,第二个报文段的序号就是1000,第三个2000,以此类推。这些序号都将被填写到TCP报文头部的序号字段中。

下面是确认号,我们知道TCP是全双工的,也就是双方都可以发送和接收数据。举例来说,A向B发送了0–999的数据,B收到该数据后,它也有数据需要发送到A,那么B发送出去的报文段中,确认号就填1000,该确认号就是A下次发送数据需要填的序号,想象一下:A的第二个报文段序号是不是1000?

2.TCP的连接(三次握手与四次挥手)

1.三次握手

其中大写的ACK是标志位,小写的ack是确认号,小写的seq是序号

1.1三次握手(Three-way Handshake)是指在建立一个TCP连接时,客户端和服务器会一共发送三个报文段。

初始时客户端和服务器都处于CLOSED状态,当服务器应用程序创建一个监听套接字时,服务器处于LISTEN状态。

1.第一次握手:客户端向服务器发送一个SYN报文段,报文段的首部中的标志位SYN置为1,另外还会指明自己的初始化序号seq=x,此时客户端处于SYN_SENT状态。

2.第二次握手:服务器收到SYN的报文段后,会以自己的SYN-ACK报文进行应答。该应答报文的首部有三个重要信息:首先SYN被置为1;其次,确认号字段ack=x+1;最后服务器选择自己的初始序号seq=y。该报文段表明:“我收到了你发起建立连接的请求,该请求报文的初始序号是x(确认号ack=x+1就表明了我收到了初始序号seq=x的报文),我同意建立该连接,我的初始序号是y。”此时服务器处于SYN_RCVD状态。

3.第三次握手:客户端收到SYN-ACK报文后,会发送一个ACK报文段,该报文段中序号seq=x+1,确认号ack=y+1,表明我已经收到了你的确认。此时客户端处于ESTABLISHED状态。

服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方以建立起了链接。

需要注意的是:第一次握手和第二次握手都只是消耗掉一个序号,但不能携带数据;第三次握手可以携带数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8wkyIvSm-1645171888031)(C:\Users\15921\AppData\Roaming\Typora\typora-user-images\image-20211023215410791.png)]

1.2三次握手的作用(为什么要进行三次握手?两次握手不行吗?)

三次握手的作用:

(1)确认双方的接受能力、发送能力是否正常。
(2)指定自己的初始化序号,为后面的可靠传送做准备。

三次握手的目的是什么?

第一次握手:客户端给服务器发送一个报文,告知服务器:“我客户端想和你建立连接”。

服务器收到第一段报文后得出结论:客户端发送功能正常,服务器接收功能正常。

第二次握手:服务器给客户端回复一个报文,告知客户端:“我服务器收到了你的请求,同意和你建立连接”。

客户端收到第二段报文后得出结论:客户端发送和接收功能都正常,服务器的发送接收功能也都正常。(但此时服务器不能确认客户端的接收功能和自己的发送功能是否正常)。

第三次握手:客户端给服务器回复一个报文,告知服务器:“我客户端收到了你的回复,知道你同意连接,那我们开始连接吧!”

服务器收到第三段报文后得出结论:客户端的接收功能和服务器的发送功能也都正常。

所以当服务器收到第三个报文后,两边就建立起了TCP连接。

那么两次握手为什么不行就显而易见了,只有前两次握手的话,服务器就不能确认自己回复的报文段是否被客户端接收到了,也就不知道自己的发送功能和客户端的接收功能是否正常了。

2.四次挥手

四次挥手也就是客户端与服务器断开连接时,需要一共发送四个报文段来完成断开TCP连接。

初始时,客户端与服务器都处于ESTABLISHED状态,假如客户端发起断开连接的请求(服务器也可以发起),四次挥手过程如下:

1.第一次挥手:客户端发送一个FIN报文段,报文段中指定序号seq=u。此时客户端处于FIN_WAIT_1状态。

2.第二次挥手:服务器收到FIN报文后,立即发送一个ACK报文段,将自己的序号设为seq=v,确认号为ack=u+1。表明已经收到了客户端的报文。此时服务器处于CLOSE_WAIT状态。

在第二次挥手和第三次挥手之间的时间段内,由于只是半关闭的状态,数据还是可以从服务器传送到客户端的。

3.第三次挥手:如果数据传送完毕,服务器也想断开连接,那么就发送一个FIN报文,并重新指定一个序号seq=w,确认号还是ack=u+1,表明可以断开连接。

4.第四次挥手:客户端收到报文后,一样发出一个ACK报文段做出应答,上一次客户端发送的报文段序号为u,那么这次序号就是seq=u+1,确认号为ack=w+1。此时客户端处于TIME_WAIT状态,需要经过一段时间确保服务器收到自己的应答报文后,才会进入CLOSED状态。

服务器收到ACK报文后,就关闭连接,也处于CLOSED状态了。

在这里插入图片描述

2.1为什么挥手需要四次?

这个问题可以换一种问法,也就是:中间两个步骤为什么不能合并呢?只要服务器收到客户端的FIN报文后,同时发送ACK报文和FIN报文不就可以三次挥手断开连接吗?

答案是通常情况下不行,因为ACK和FIN的触发时机是不一样的。我们要搞清楚一件事:服务器在收到FIN报文后可以立即发送ACK报文,表明我服务器收到了你的报文;但是服务器想发送FIN报文就需要等到处理完接收缓冲区的数据后才可以。所以挥手需要四次。

2.2为什么要等待2MSL?(为什么会存在一个TIME_WAIT状态?)

报文段最大生存时间MSL(Maximum Segment Lifetime),它是任何报文段被丢弃前在网络内的最长时间,超过这个时间报文将被丢弃。等待2MSL是为了确保服务器收到了最后一段ACK报文。

如何确保?

如果服务器没有收到最后一段ACK报文,就会触发超时重传:服务器会再次发送FIN ACK报文。那么在2MSL的时间内,客户端会再次收到FIN报文,客户端就知道刚刚发送的ACK丢失,需要再次发送。

如果服务器收到了最后一段ACK报文,客户端在2MSL的时间内就不会收到任何报文,客户端就知道刚刚发送的ACK报文没有丢失,不需要再次发送,可以安心进入CLOSED状态了。

3.可靠数据传输

3.1.超时重传与超时时间加倍

在大多数TCP实现时,每当超时事件发生时,TCP会将下次的超时间隔设置为先前值的两倍,并重传刚刚的数据。

例如,A发送给B一段数据,定时器启动,设置的超时时间为0.75s,结果A在0.75s内没有收到确认应答,此时A就会重传刚刚的数据并将超时时间设置为1.5s。因此超时间隔在每次重传后会成指数型增长。

3.2快速重传

超时触发重传的问题之一是超时间隔可能过长,因而增加了端到端的时延。如果发送方接收到对于相同数据的三个冗余ACK,它就认为跟在这个已经被确认三次的报文段之后的报文丢失了,那么发送方就会执行快速重传,即在该报文段的定时器过期之前重传丢失的报文段。

冗余ACK就是再次确认某个报文段的ACK,而发送方之前已经收到了该报文段的确认。

接收方为什么会连续发送对于同一个报文段的确认?当接收方接收到了0–999的报文段之后,他期望的下一个报文段序号应该是1000,但是此时它却收到了序号为2000的报文段,那么他就会立即发送冗余ACK,该ACK的确认号是1000,表明接收方想要收到的是序号为1000的报文段。但接下来他又收到了序号为3000的报文段,接收方会再次发送冗余ACK,以此类推,当发送方收到三个连续的冗余ACK时,就会执行快速重传。

3.3选择重传

选择重传就是说,接收方收到了失序的报文段后,就说明可能出现了报文段的丢失,它会发送冗余ACK来让发送方再次发送丢失的报文段;之所以不是发送丢失报文段之后的所有报文段,是因为TCP会将收到的失序报文段进行缓存而不是直接丢弃,所以在收到丢失报文段之后再排序接收就可以了

4.流量控制

首先要知道,TCP连接的主机后都有接收缓存,数据到达后先待在缓存中,等待应用进程的读取,然而缓存空间有限,当进程读取数据相对缓慢,而发送方又发送的太多太快时,就需要流量控制来工作了。

再次想到TCP报文段中的接收窗口字段。该字段中的值就是接受方的空闲缓存空间的大小,发送方通过该字段来控制自身的发送速率。

但是想象一下这种情况:当发送方收到接受方的报文段后,发现接收窗口字段的值为0!那么发送方就一直停到那里不发送数据了吗?当然不是,它会继续发送只有一个字节数据的报文段,当接受方的缓存被读取后就会出现空闲空间,这时这种报文段就会被接收,发送方收到接收确认后就会接着发送报文段了。

5.拥塞控制

5.1发送方如何感知网络拥塞?

首先明确一个名词,丢包事件:超时,或者发送方收到三个冗余的ACK。

当网络过度拥塞时,一段数据包就会被路由器丢弃,那么就会触发发送方的丢包事件,当发送方发现丢包,就认为网络中发生了拥塞。


以下将介绍拥塞控制算法的三个部分:慢启动、拥塞避免和快速恢复

在开始之前,明确两个名词:拥塞窗口和慢启动阈值

拥塞窗口(cwnd):是指发送方能向网络中发送的数据量。

慢启动阈值:其实就是cwnd的阈值,当cwnd到达该值时,会结束cwnd的指数增长。

5.2慢启动

当一条TCP连接开始时,cwnd=1,表明只发送一个报文段,当响应ACK在规定时间内到达,发送方将拥塞窗口设置为cwnd=2,并发送两个报文段,当再次收到确认ACK时,将拥塞窗口设置为cwnd=4…以此类推,直到发生结束。

拥塞窗口的指数增长速率在三种情况下会结束:

(1)超时,发生超时后,发送方将阈值设置为cwnd/2,也就是拥塞窗口的一半;同时将cwnd设置为1,从头慢启动。

(2)窗口大小>=阈值,结束慢启动,并进入拥塞避免模式。

(3)检测到3个冗余的ACK,发送端执行快速重传,并进入快速恢复状态。

5.3拥塞避免

刚刚提到,当窗口大小>=阈值时,进入拥塞避免状态。一旦进入该状态,cwnd的值就变为原来的一半,此时拥塞窗口呈线性增长,也就是说,每个传输轮回,cwnd只加1。

拥塞避免在以下情况下会结束:

(1)超时,与慢启动情况一样,发生超时后,发送方将阈值设置为cwnd/2,也就是拥塞窗口的一半;同时将cwnd设置为1,

(2)检测到3个冗余的ACK,发送方将阈值设置为cwnd/2;但是将拥塞窗口大小也变设为cwnd/2,而不是变成1,接下来进入快速恢复状态。

5.3快速恢复

当发生丢包事件(超时或者收到三个冗余的ACK)时,发送端设置 :阈值=cwnd/2,同时窗口大小=阈值,然后进入拥塞避免状态,cwnd将线性增长。

以上是我总结的全部内容,如有纰漏请指正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值