3. 连接管理
四次挥手
第六位 FIN 结束报文段
建立连接,一般都是客户端发起的
断开连接,客户端和服务器都可以主动发起
此时断开连接,就相当于是A和B都把对端的消息删除了
和三次握手不同,此处的四次握手,不能把中间的两次交互合并
——> ACK 和第二个 FIN 的触发时机不同
ACK 是内核相应的,B收到 FIN,就会立即返回 ACK
第二个 FIN 是应用程序的代码触发的。B调用close方法才会触发 FIN
FIN 会在socket对象close的时候被发起,可能是手动调用close方法,可能是进程结束
三次握手,ACK 和 syn 都是内核触发的。同一个时机,可以合并
四次握手,ACK 是内核触发的,第二个 FIN 是应用程序执行close触发的。时机不同,不能合并
此处和超时重传类似:
1/2 丢失,A会重传 1
3/4 丢失,B会重传 3
4. 滑动窗口(效率机制)
前面的机制,都是在保证TCP的可靠性
TCP的可靠传输,是会影响传输的效率的(多出了等待ack的时间)
TCP引入了可靠性,传输效率不可能超过UDP
TCP的“效率机制”都是为了减小影响,尽可能的提高效率
此处不等ack回来,直接发送下一个数据
批量传输,不是”无限的“传输
批量传输存在一定上限,达到上限后,再统一等待ack
不等待的情况下,批量最多发送多少数据,称为“窗口大小”
上图中就是A—>B批量的发送了四份数据,此时B也要给A回应四组ack
当A已经达到窗口大小,在收到ack之前,不会继续发送数据
需要等待ack回来之后才能继续往下发送
此处A的发送机制:收到一个ack,就立即发送一份数据
窗口越大,等待的ack越多,传输的效率也就越快
丢包问题
1)ack丢失
此时不需要任何重传
确认序号,表示的含义是,当前序号之前的数据已经确认到了,下一个应该从确认序号这里,继续发送
如果1001的ack丢失,但是2001的ack收到了
表示2001之前的数据都已经确认收到了,涵盖了1001的情况
如果ack全部丢失——>
此时相当于是网线断开,数据无法传输
2)数据包丢失
主机A需要知道那个数据丢失了,主机B就得需要告诉主机A那个数据丢失了
由于前面的1001-2000这个数据丢失,此处返回的ack仍然索要1001.无论当前传输的数据具体是几,都在索要1001这个数据
此时,主机A看到主机B返回的连续的几个ack都在索要1001.A就知道1001这个数据丢失了,就重传1001
1001-2000重传后,顺利达到。主机B索要的就是4001
上述重传的过程中,没有额外的冗余操作。那个数据丢了,就重传那个数据,没丢的数据就不需要重传,整个过程比较快速 ——> 快速重传(滑动窗口下,超时重传的变种)
如果通信双方,传输的数据量比较小,也不频繁,仍然是普通的确认应答和普通的超时重传
如果通信双方,传输的数据量比较大,也比较频繁,就会进入滑动窗口模式,按照快速重传的方式处理
通过滑动窗口传输数据,效率会有一定的提升(不能无限放大)
窗口越大,传输效率越大(一份时间,等待的ack多了,总的等待时间就少了)
如果传输数据的速度太快,接收方可能就处理不过来,此时接收方也会出现丢包,发送方就得重传
TCP前提是可靠性,在可靠性的基础上,再提高传输效率
5. 流量控制
接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应 因此 TCP 支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做 流量控制( Flow Control )TCP socket对象上带有接收缓冲区
数据会到达B的系统内核中
A—>B的数据,会先到达B的接收缓冲区
B这边的应用程序,就会调用read的方法,把数据从接收缓冲区读出来,进一步的处理(一旦数据被read,就从接收缓冲区中删除了)
此过程类似于生产者消费者模型——>
生产者:A
消费者:B的应用程序
交易场所:B的缓冲区(相当于是一个阻塞队列)
消费速度,就是数据的处理能力,该概念可以量化为——> 接收方缓冲区的剩余空间的大小
剩余空间越大,意味着消费速度越快,处理能力越强
接收方每次接收数据后,都会把接收缓冲区剩余空间的大小,通过ack返回给发送方,发送方就会按照这个数值调整下一轮的发送速度
16位窗口——>接收缓冲区的大小
TCP报头里,选项部分有一项是叫做“窗口扩展因子”(可扩展窗口的大小)
当B返回给A的报文中缓冲区的剩余空间为0时,表示接收方已满,发送方暂停发送
在A暂停时候,不会传输业务数据,会周期性的传输一个“窗口探测包”,并不携带具体的业务数据
探测报只是为了触发ack,查询当前接收方这边的接收缓冲区的剩余空间
6. 拥塞控制
考量通信过程中中间节点的情况
中间的转发过程中,任何一个节点,处理能力达到上限,都可能会对发送方产生影响
由于中间节点结构更复杂,难以进行量化,因此使用“实验”的方式来找到合适的值
让A先按照比较低的速度先发送数据(小的窗口)
如果数据传输顺利,没有丢包
再尝试使用更大的窗口,更高的速度进行发送(慢慢加速)
随着窗口增大,中间节点可能出现丢包,此时调小窗口,如果不丢包,就继续尝试增大窗口
在这个过程中,发送方不停的调整窗口的大小,逐渐达成“动态平衡”
相当于是把中间节点当成一个整体,通过实验的方式,找到中间节点的最大值
拥塞窗口:拥塞控制下,发送方应该按照多快的速度(窗口大小)进行传输数据
传输轮次:当前是TCP是第几次发数据
刚开始,发送的速度非常慢(慢开始/慢启动)
指数规律增长:就算一开始很慢(慢开始),接下来的几轮内,窗口就会变得很大
线性增长:传输速度会越来越快,增长到一定程度就会出现丢包
网络拥塞:触发丢包,重复前面的 慢开始 —> 指数增长 —> 线性增长
并会根据当前丢包的窗口大小,重新指定线性增长的阈值
后来的TCP又进行了改进——>
网络阻塞后不会回到慢开始,而是会一个新的阈值,线性增长
流量控制和拥塞控制都是限制发送方的窗口大小
最后发送方的窗口大小取决于 流量控制 和 拥塞控制 中的窗口的较小值
7. 延迟应答
A把数据传输给B,B会立即返回ack给A(正常)
有时,B会等一会再返回ack给A(延迟应答)
本质上是为了提升传输效率,发送方的窗口大小,就是传输效率的关键
流量控制就是根据接收缓冲区的剩余空间,决定发送速率
如果在接收方能处理的前提下,使流量窗口尽可能变大,效率也会尽可能变大——>
延时返回ack,给接收方更多时间,来读取接收缓冲区的数据
此时接收方读取了这个数据后,缓冲区的剩余空间就变大了
返回的窗口大小也变大了
如果接收缓冲区的大小是18kb,立即返回ack,返回了18kb的窗口大
此时延时20ms再返回,这20ms内,接收方的应用程序又读取了4kb的数据
此时ack返回的窗口大小就为22kb了
8. 捎带应答
在延迟应答的基础上进一步提高效率
网络通信中,一般是“一问一答”的通信模式:
request 和 response 是业务上的数据
ack是内核即可返回的,response是应用程序代码返回的,两者的返回时机不同
由于TCP的延时应答。第一个ack不一定立即返回,可以等一会。在等待的过程中,B可能把response的值计算好了,可以把response返回了。此时可以顺便把刚才要返回的ack也带上
本来要传输两个TCP数据包(封装分用两遍)
通过上述操作,可以把两个包合二为一,可以达到更高效的效果
9. 面向字节流
此时还有一个重要的问题——>粘包问题(面向字节流的机制都有类似的问题)
此处的“包”为应用层数据包,如果同时有多个应用层数据包被传输过去,此时很容易出现粘包问题
此时传输三个应用层数据包
目前,接收缓冲区中,这三个应用层数据包,以字节的形式紧紧挨在一起
接收方读取数据的时候,可以一次读一个字节,也可以一次读N个字节
最后的目的是获取完整的应用层数据报
B应用程序,不知道缓冲区里的数据,从哪到哪是一个完整的应用层数据包
UDP面向数据报的通信方式就没有上述问题
UDP的接收缓冲区,相当于是一个一个的DatagramPacket对象
应用程序读的时候,就能明确的知道从哪到哪是一个完整的数据包
——>通过定义好应用层的协议,明确应用层数据报之间的边界
1)引入分隔符
使用 \n 为分隔符
2)引入长度
每次固定前两个字节声明数据包的长度
应用程序在读取数据的时候,可以先读取两个字节,得到数据包的长度,再根据数据包的长度,继续读取对应字节的个数
3)
自定义应用层协议的格式
xml json protobuffer...本身都是明确了包的边界的
10. 异常情况的处理
1)进程崩溃
进程没了,异常终止了。文件描述附表,也就是放了,相当于调用 socket. close()
此时会触发FIN,对方收到后,自然会返回FIN和ACK,这边再进行ACK(正常的四次挥手)
TCP的连接,可以独立于进程存在(进程没了,TCP连接不一定)
2)主机关机(正常流程)
在进行关机的时候,会先触发强制终止进程操作(相当于1.)
此时会触发FIN,对方收到后,会返回FIN和ACK
进程没了,系统也可能关闭。如果系统还没关闭,对端返回的ACK和FIN到了,系统还是可以返回ACK,进行正常的四次挥手;如果系统已经关闭,ACK和FIN迟到了,无法进行后续的ACK响应,站在对端的角度,对端以为是自己的FIN丢失了,重传FIN。重传几次没有响应,就会放弃连接(把持有的对端的消息删除)
3)主机掉电(非正常)
此时是一瞬间的事,来不及删除进程,也来不及发送FIN,主机直接停机了
站在对端的角度,并不知道发生了什么:
1. 如果对端是在发送数据(接收方掉电),发送的数据就会一直等待ACK,触发超时重传。触发TCP连接重置功能,发起“复位报文段”,如果“复位报文段”发过去之后,也没有效果,此时就会释放连接
RST 复位报文段
2. 如果对端是在接收数据(发送方掉电),对端还在等待数据到达。此时无法区分,是对端没发消息,还是对端挂了
TCP中提供了“心跳包”机制:
接收方会周期性的给发送方发起一个特殊的,不携带业务数据的数据包,并期望对方返回一个应答。如果对方没有应答,并且重复了很多次后,仍然没有,就视对方挂了。此时就可以单方面断开连接了
4)网线断开
和主机掉电类似
假设,是A正在给B发送数据,一旦网线断开
A就相当于是会触发超时重传——>连接重置——>单方面释放
B会触发心跳包——>发现对端没响应——>单方面释放
标签:窗口,重传,05,ack,网络,TCP,发送,原理,FIN From: https://blog.csdn.net/2301_80055001/article/details/144475809TCP的心跳包机制:
日常开发中经常看见这种心跳包机制(尤其是分布式系统)
一般通过心跳包机制,识别某个机器是否是挂了
一般后续使用到的心跳包都是应用程序里自动实现的,而不是直接使用TCP的心跳包
心跳包希望是秒级甚至是毫秒级检测出对端是否存活。TCP的心跳包周期比较长