TCP的核心属性
- 一: TCP的核心属性
- 1.1: 确认应答:
- 1.2 : 超时重传
- 1.3 : 连接管理
- 1.3.1 三次握手
- 1.3.2 四次挥手
- 1.4 滑动窗口
- 1.5: 流量控制:
- 1.6 拥塞控制
- 1.7 延时应答
- 1.8 :捎带应答
- 1.9: 面向字节流
- 1.10 : 异常情况
一: TCP的核心属性
1.1: 确认应答:
保证可靠性最核心的机制
1.2 : 超时重传
针对确认应答的机制的核心补充.
1.3 : 连接管理
- 建立连接:三次握手
- 断开连接: 四次挥手
握手,挥手发送的是不带业务数据的数据包.
连接: 指的是保存对端的信息.
建立连接 ,就是让双方把对方的信息给记录下来.
断开连接 : 让双方把对方的信息删除掉.
1.3.1 三次握手
三次握手的意义:
1: 投石问路: 确认通信链路是否畅通
2: 验证通信双方的发送能力和接收能力是否正常.
3: 协商核心数据
==两个重要的状态: ==
1: LISREN :
服务器进入的状态,端口绑定成功,服务器准备就绪.
2: ESTABLISHED:
连接建立成功,随时可以进行数据通信了.
1.3.2 四次挥手
为啥四次挥手是四次?可以合并吗?
1)不能合并.ack 是内核收到fin 立即返回的,fin 则是代码中调用close才返回的.
2) 可以合并,TCP的延时应答机制.
==四次挥手中的重要状态: ==
CLOSE_WAIT : 收到fin 到应用程序调用close方法中间所处的状态称为CLOSE_WAIT状态.
TIME_WAIT : 存在的意义 ,就是为了应对最后一个ACK 丢包这样的场景.
CLOSE_WAIT: 被动接收的一方所处的状态.
TIME_WAIT : 主动发起的一方所处的状态.
TIME_WAIT等待的时间一般是2MSL.
四次挥手只是通知,我要把你删了,真正删除信息是在四次挥手结束,就相当于双方在离婚协议书上签字,签完字之后,再把对方的练习方式删除.
== 如果发现服务器出现大量的TIME_WAIT ,说明了什么?==
出现了大量的TIME_WAIT ,说明服务器触发了大量的主动断开TCP连接的操作,这样的操作对于服务器来说,很可能是不科学的,一般都是客户端主动断开连接.
1.4 滑动窗口
滑动窗口是一种提高传输效率的机制.
- 没有滑动窗口的情况:
-
引入滑动窗口机制 :
滑动窗口中,批量发了4 组数据之后,不是等到4 个ACK 都回来才继续发送新的数据,而是收到一个ack ,就往后发一个新的.
滑动窗口,如果出现丢包咋办? -
数据包已经到达接收方,但ACK丢了:
不需要做任何处理:
-
数据包直接丢了:
只需要把缺失的数据重传即可,其他的数据不必重传.
一旦缺口补上,接下来就可以从队列中最后一盒数据的序号继续往后发送数据包即可.
== 超时重传和快速重传 , 相当于两种不同的重传机制,是否是冲突的呢?
不冲突, 快速重传相当于超时重传在滑动窗口的特殊变种.
如果TCP 传输的数据比较少, 不频繁 , 此时就不会触发滑动窗口, 如果短时间传输大量的数据 ,此时才会触发滑动窗口.
即使引入滑动窗口,使TCP的传输效率有一定的提升,但速度不可能比UDP这种没有可靠性机制的协议更快.
1.5: 流量控制:
指的是滑动窗口的大小.
窗口越大, 单位时间发送的数据就越多,窗口越小,单位时间发送的数据就越少.
通常情况下,希望尽可能高效的传输,但高效的前提是可靠性,但接收方的接收能力有限,不能即使处理这些数据,就会造成丢包现象的发生.
所以,更合理的做法,应该是 接受方根据自身的处理能力,反向制约发送方的速度,使双方达成一个"平衡".
1.6 拥塞控制
和流量控制类似,都是和滑动窗口搭配的机制.
流量控制,是站在接收方的角度,影响发送方的速度,而拥塞控制,是站在数据链路层,链路上的任何一个节点,窗口太大,都可能造成丢包现象.性能瓶颈都会制约发送方的发送速度.
如果考虑中间的每一个节点,就变复杂了:
因为中间链路有很多设备,以及走的路径都可能不同 ,每个设备处理能力,繁忙程度都可能不相同.
虽然中间结构很复杂,但TCP把他们视为一个整体,然后通过实验的方式,找到一个合适的窗口大小.
上图过程,就称为 " 拥塞控制".
== 窗口大小以拥塞控制计算的窗口大小为准还是流量控制计算出来的窗口大小 ?==
哪个计算出来的窗口小,就以哪个为标准!
1.7 延时应答
指的是ACK不会立即返回,而是稍等一会再返回.
为啥要延时???
核心目的,就是提升传输的效率----决定传输速度最关键的因素,就是窗口大小,在能够承受的前提下,尽可能的提高窗口大小.
通过延时机制,就可以 使窗口大小得到提升,延时就是给应用程序腾出来更多的时间来消费数据包.
延时时间,有两种方式:
1)按照一定的时间来指定延时,
2) 按照收到的数据量.
往往这两个策略是结合使用的.
1.8 :捎带应答
建立在延时应答的基础之上,提升效率的机制.
正常情况下,ack是内核收到请求,自动返回的,而响应是在程序执行一系列逻辑之后,再返回的.
由于延时应答机制的存在,ACK不一定立即返回,在ACK稍等这一会的时候,正好,就要返回响应数据,此时,就在响应数据中,TCP的ACK这一位置上,把确认序号,窗口大小都设置上.
这样,就把两次传输合并成了一次传输,提高了传输效率.
三次握手的合并是由于捎带应答机制的原因吗?
不是,三次握手,本来就是相同时机,本来就是一个报文就可以解决,而捎带应答则是在延时应答的基础上,本来不能合并,延时之后,才能合并.
1.9: 面向字节流
在字节流读写数据的场景中,会涉及到一个非常关键的问题: 粘包问题
比如接收方的阻塞队列中,有一下数据:
json ,xml,yml 都是基于分隔符的方式来进行区分的.
protobuffer按照长度的方式来区分的.
1.10 : 异常情况
1). 其中某个进程崩溃了
进程崩溃也好,正常结束也好,操作系统都能够回收释放对应的PCB,可以释放里面的文件描述符表,也就相当于调用了close方法.
也就是四次挥手能够正常挥完,虽然进程不在了,但操作系统仍然管理着TCP的连接,所以可以和对方挥完.
2) . 某个主机被关机(正常流程的关机)
对于正常流程的关机,操作系统会先尝试强制结束所有的用户进程,然后在进入关机流程.
如果四次挥手没挥完, 系统就关机了:
比如: A和B建立了TCP连接,A这边关机了,A关机之前,告诉B,发送fin,B这边收到了fin,B返回ACK,代码进入下一阶段流程,准备发送fin,但如果A关机了,也就意味着B 接下来的 fin 不会收到ACK,B的fin就会反复重传几次, 如果B仍然没收到ACK,还是继续把A给删除, A都已经关机了,之前保存的B的信息,自然也没了.
3) 某个主机突然掉电(非正常关机)
A和B通信, A突然掉电了.
A无法做出任何反应,而B仍然认为,A还存在呢.
a)B是发送数据方:
B接下来发的数据,都不会有ACK,B就会触发超时重传,重传几次之后,发送复位报文(RST),RST也没有反应,B就会单方面删除保存A的信息
b) B是接收方:
接收方,无法知道对方啥时候给我发送数据
当A沉默之后,B也不知道A是暂停一会,还是A挂了
B在一定时间之内没收到A的数据之后,就会触发心跳包
心跳包,就可以认为是一个没有载荷的数据包,只是为了触发ACK
B给A发了一个心跳包,如果A正常,A就会回应ACK,如果A挂了,B就不会收到任何回应.连续发了若干次,A都没有回应,这个时候B认为A挂了,于是单方面是否连接.
TCP虽然内置了心跳包,但是这个心跳包,周期比较长,指望通过这个心跳包发现对端挂了,往往需要分钟这样的时间.
在实际开发中,经常会实现应用层的心跳包,用更高频率,更短周期发送心跳.
- 网线断开
A和B之间的网线断开了.
比如: A是发送方,B是接收方
A的角度,就会触发超时重传,触发RST,单方面删除信息.
B的角度,就会触发心跳包,对方无响应,单方面删除信息.