TCP协议
TCP协议
1.TCP协议格式段
TCP是一种面向连接,可靠的,基于字节流的传输层协议,它提供了可靠的、全双工的数据流传输.
- 源/目的端口:表示数据从哪个进程来,到哪个进程去.
- 32位序号/32位确认序号
- 32位序列号:表示本段数据的第一个字节的序列号.用于实现顺序传输和重传机制.
- 32位确认号:如果设置了ACK标志,则这个字段包含了对方期望接收的下一个序列号.用于确认收到的数据.
- -4位首部长度:4位,以4个字节为单位,TCP头部长度可变,表示头部大小,不包含载荷,从而可以找到数据的起始位置TCP最大长度是60字节.
- 保留:为后续使用
- 控制位:包括URG,ACK,PSH,RST,SYN和FIN等标志位,控制TCP连接的状态和行为.
- 窗口大小:发送端期望接受的数据量,用于流量控制
- 校验和:检测TCP数据的错误
- 紧急指针:在URG标志被设置时有效,紧急数据的末尾
- 选项:可选字段,包括最大段大小.
2.TCP原理
2.1 确认应答机制(可靠性)
发送方会发送一条数据给数据接收方,验证接收方是否收到数据接收方会返回一条数据给发送方,就能确保接收方成功收到数据.
但是如果连续发送多条数据的时候,就有可能会出现"后发先至"(一个数据报,是先发的,另外一个是后发的,后发的数据反而先到了)的情况.
例如:在正常情况下:
如果出现了先发后至:
我们预期的情况是:主机B的回复顺序是:先接受了主机A的吃饭邀请,再回复第二条拒绝帮写作业,但是由于先发后至,主机B的回复顺序变为了:先拒绝主机A的吃饭邀请,再回复第二条接受帮写作业.
通过上述的描述,我们可以发现错乱应答的现象,但是这种情况我们无法避免,为了解决这一问题,我们就引入了序列号和确认序列号.通过序列号来对此传输的数据进行序列号对比,再做出回应,就不会出现错乱应答的问题了.TCP将每个字节的数据都进行了编号。即为序列号
主机A发送数据的字节序号位(1-1000),主机B返回1001,表示1001之前的数据主机B都受到了,接着就是从1001继续开始发.
只需要在TCP报头中,通过32位序列号,把这一串的第一个字节表示出来,再结合报文的长度,每个字节的编号也就确定了.
如何确定当前这个报文时普通报文还是确认应答报文?
ACK为0的表示是一个普通的报文,只有32位序号是有效的,如果超出了32位序号,就会重新开始算
ACK位1的表示是一个应答报文,这个报文的序号和确认序号都是有效的.
TCP保证可靠性的核心机制是:确认应答
2.超时重传机制(可靠性)
- 主机A发送数据给B之后,可能会因为网络堵塞等原因,导致数据无法到达主机B
- 如果主机A在一个特定的时间内没有收到B发送来的确认应答,就会进行重发
但是,主机A未收到B发送过来的确认应答,也可能是ACK丢失了,同样也会进行重发,因此主机B可能会收到很多的重复的数据,重复的数据如果不及时处理,就会影响数据的正确性.
接收方收到数据之后,需要对数据进行去重,把重复的数据丢弃掉,保证应用程序读到的数据不会出现重复.去重的方式可以通过TCP序号来作为判定依据.TCP会在内核之中,给每一个Socket对象都安排一个内存空间,相当于一个队列,也被称为"接受缓冲区",收到的数据,都会被放到接受缓冲区里,并且按照序号排列好顺序就行.
超时重传的等待时间多久
超时重传的时间不是一个固定的值,而会随着超时轮次的增加,等待的时间也会进一步增长,如果重传几次都没有成功,说明网络本身的丢包率是极高的,当重传的轮次达到一定程度,也就放弃,此时就会"重置TCP连接",也就是TCP的复位报文"RST",如果RST为1表示是一个复位报文,如果复位操作也无法成功,就会放弃连接,把自己保存的对端信息给删除掉.
3.连接管理机制(可靠性)
在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接.
三次握手的目的是确保双方都能正常的手法数据,以建立可靠的连接.
SYN:申请建立连接的请求,如果为1,就是同步报文段,就是一台主机就要尝试和另一台建立连接.
三次握手的初心:
- 确认双方的通信能力:在建立连接之前,客户端和服务器之间需要通过三次握手来确认彼此的存在和可用性.这确保了双方都准备好进行通信,并且能够响应对方的请求.
- 同步双方的初始序列号:TCP中的每一个数据包都包含一个序列号,用来对数据包进行排序和重组,通过三次握手,双方可以协商通过序列号,确保数据包能够正确的按顺序抵达.
- 防止旧的数据的干扰:网络上传输消息,可能要要等很久,当消息达到对端的时候,服务器可能已经和客户端断开连接了.为了防止上个数据的干扰,就可以通过序列号来识别是旧的消息还是新的消息,如果是旧的消息,那么就可以丢弃了.
四次挥手的目的确保双方都可以正确的关闭连接,没有数据残留在网络中流动.
FIN:连接的关闭请求和确认,当一方希望关闭连接时,会设置FIN的标志位为1.
四次挥手能否三次就完成?
不一定.
其中ACK是内核控制的,而FIN是用户代码控制的,FIN的触发,是通过应用程序代码实现的,当调用socket.close()或进程结束,就会触发FIN,如果服务器有很多的工作需要完成,close此时就不能执行,无法和上一个ACK合并,也就不能完成三次挥手.
如果服务器不进行close,服务器的TCP状态就会处于CLOSE_WAIT状态.
站在服务器的角度,虽然连接没有关闭,但连接已经不能正常使用,针对当前socket进行读操作,数据还没有读完(缓冲区还有数据),能正常读取到的,如果数据已经读完了,就遇到EOF,对于字节流来说,就会返回-1,如果是Scanner或者hasNext就会返回false.
如果close忘写,站在客户端的角度,会收不到武器的FIN,也会进行等待,如果一直等待都等不到,此时就会单方面放弃连接.
通信过程中,如果出现丢包怎么处理?
此时会尽可能重传,如果重传多次都失败,就会单方面释放连接,如果是第一组FIN/ACK丢失,重传就行,但是如果第二组的FIN和ACK丢失了,就需要注意,如果说当客户端收到服务器发送过来的FIN并且发送了ACK,客户端就认为四次挥手已经结束了,但是客户端这边还不能释放,因为最后一个ACK丢失了,服务器就会重新传入过来一个FIN,如果客户端已经释放了,重传的FIN就无法进行ACK了.
因此就需要客户端在发送完最后一个ACK之后,在等待一会,等待一定时间之后如果还没有重新传入FIN就会被认为ACK已经接收到了,具体等待的时间就是网络上任意两点之间传输数据的最大时间*2,被定义为MSL.
4.滑动窗口(效率)
滑动窗口是为了提高传输效率.
确认应答策略是对每一个发送的数据段,都要给一个ACK确认应答,收到ACK后再发送一个数据段,这样做有一个比较大缺陷,就是可能数据往返的时间可能会比较长.
采用滑动窗口我们就可以批量发送多条数据.这样就可以缩短等待时间.
如果出现丢包怎么处理?
- 数据丢了:在接收方会有一个缓冲区在接受数据,如果缓冲区少了一块,返回的ACK就会始终索要1001这个数据报,一旦1001这个数据报被补上了,1001-2000后的数据就不必重新传输了(都存在了缓存区中),后面就查看数据是否还有空缺,有的话就索要对应的数据,没有的话就索要缓冲区最后一条数据.
- ACK丢了:如果只是丢了一部分的ACK,对于可靠传输是没有影响的.因为可以通过后续的ACK就可以确认.比如1001返回的ACK丢了,但是2001的ACK没有丢,说明2001之前的数据都收到了
5.流量控制(可靠性)
流量控制避免窗口太大,接收方处理不过来.
根据接收方的处理能力,来限制发送方的发送速度.衡量接收方的处理速度就要使用到接受缓冲区额剩余空间的大小,如果剩余空间越大,应用程序消费数据的速度就越快.接收缓冲区剩余空间的大小会通过ACK报文反馈给发送方,作为发送方的下一次数据,窗口大小为64kb,但是可以根据选项中的窗口扩展因子左移.
6. 拥塞控制(可靠性)
有了滑动窗口,能够可靠的发送大量的数据,但是如果一开始就发送大量的数据,可能会存在问题,因为网络状态上可能会比较拥堵,再不清楚当前网络状态下,突然发送大量的数据,可能会导致数据丢包.
TCP拥塞控制的流程:
- 慢启动:TCP引入了慢启动机制,先发送少量的数据,探清当前的网络状态,然后再决定按照多大的传输速度传输.
- 指数增长:在传输顺畅的情况下,拥塞窗口会指数增长.
- 线性增长:指数增长当到达一定的限制之后,会按照线性方式增长,转换为线性增加(+n).
- 拥塞窗口回归小窗口:当窗口增长过程中,如果数据出现丢包,就会认为网络出现拥堵,此时就会把窗口调整成小窗口,继续回到指数增长+线性增长的过程.另外也会根据当前出现丢包的窗口大小,调整阈值.
指数增长和线性增长是按照传输的轮次来增长.假设给定的窗口是2000,2000的数据发送完成后,就表明这一轮已经完成了.
新的阈值=出现丢包的窗口大小/2
实际发送的窗口=min(拥塞窗口,流量控制窗口)
7.延迟应答(效率)
延迟应答:接收方接收到数据包后不立即确认发送ACK而是等待一段时间发送.利用这个等待的时间,就可以给应用程序更多的消费数据的时间.
此时立即返回ACK那么返回的窗口大小就是3KB,如果等待一会,应用程序可能会消耗掉2KB,那么返回的窗口大小就是5KB.通过延时应答能够提高多少速度,却决于应用程序的实际处理能力.
8.捎带应答(效率)
捎带应答:发送数据的时候,携带确认信息(ACK)一起发送,而不需要仅包含确认信息的数据包.
数据报从两个合并成一个,效率会有所提升,因为每次传输数据都需要封装和分用.能合并的原因是ACK数据本身不需要携带载荷,与正常的数据也不冲突,所以说可以让数据报即能携带载荷又能带有ACK的信息.
9.面向字节流
TCP是面向字节流的,但是在面向字节流的情况下,可能会产生一些问题 - 粘包问题(粘-应用层数据报)
通过TCP中read/write的数据,都是TCP报文的载荷,也就是应用层数据.发送发可以一次性发送多个应用层数据报,但是在接受的时候,如何看出是一个完整的应用层数据报,如果说发送的是两个包,但是可能读的时候是半个或者说一个半.
为了解决这种问题,我们就可以合理设计应用层协议:
- 应用层协议中,引入"分隔符",区分包之间的边界.
2. 应用层协议中,引入"包长度",区分包之间的边界.
10.TCP异常情况
网络本身可能会存在一些变数.从而导致TCP不能正常工作.
- 进程崩溃:进程就销毁了,PCB进程控制块也会销毁,也就导致文件文件描述符表被释放了,相当于调用了socket.close,此时崩溃的一方会发出FIN,触发四次挥手.连接就会被正常释放.
- 主机关机:正常关机会销毁所有进程,崩溃的一方发出FIN,也会触发四次挥手,如果四次挥手没有挥完,那么有一端就会收不到ACK就会重传,重传几次都不行,就会单方面释放自己连接的信息.
- 主机掉电:会分为两种情况,第一种是接收方掉电;第二种是发送方掉电.
接收方掉电) 如果发送方给接收方发消息,发送方发的消息,没有ACK,就会触发超时重传,重传失败就会触发复位报文RST,尝试重新连接,重新连接失败就会单方面释放.
发送方掉电) 如果发送发给接收方发消息,接收方在等发送方的消息,发送方突然不发消息了,此时接收方不知道发送方的情况,就会陷入阻塞等待,此时就需要一个"心跳包".如果发送发不能返回ACK就说明发送方已经被销毁.
心跳包:为了触发ACK,确认对方端是否正常工作/确认网络是否通畅.
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!