TCP 协议的相关特性

TCP协议是有连接的,可靠传输,面向字节流,全双工的,针对这些特性,介绍一下TCP协议的10个较为核心的机制

一.确认应答

确认应答是TCP可靠通信的最核心机制,TCP是可靠传输,可靠性指的不是数据100%能到达对方,而是尽可能的传输过去,所以要确认数据是否真的到达对方了

这里接收方会返回一个应答报文(ACK),会引入序号和确认序号的概念,建立应答报文和传输的数据之间的对应关系

序号:按照字节编号,连续递增。确认序号:序号的最后一个字节+1.

例如:A给B发送了1-1000的数据序号,B返回序号1001,这里就代表B告诉A,1001以内的数据我都收到了,接下来要1001后面的数据了

二.超时重传

超时重传是对确认应答的一个重要的补充,组成正常情况下,TCP是通过确认应答来知道数据是否被对方收到了,A给B发消息,如果B没有收到A的数据,此时B不可能给出任何应答报文,如果A没有收到B返回的ACK(应答报文),此时A就可以判断出现了丢包。

A从发送数据之后,到正常收到ACK,肯定也会经历一些时间,A就会进行一定的等待,如果等待时间超过了某个阈值,此时就可以认为出现丢包,发现丢包后,进行重传,把刚才发的数据再传输一遍,如下图所示

如果数据报顺利到达,但是返回的ACK丢了捏?

其实站在 A 的角度, A 无法区分是 数据丢了,还是 ACK 丢了,A 能看到的都是 没有收到 ACK!!!
A 做的事情只能是触发重传!!

很明显,B 此时就收到了两份重复的数据!!上述情况,是不科学的!!对于转账这样的操作,触发重传就引起 重复转账,非常糟糕的问题,

此时上文提到的序号就派上了用场,TCP接受方这边会按照序号进行去重,将相同的去除

超时重传,超时时间,是多少呢?

注意,这个时间,不是固定数值,而是会动态变化,随着重传轮次的增加,会变的越来越长
假设第一次传输数据,等待 50ms,,就触发重传重传之后, 等待 100ms,如果还是收不到 ack再等待,再等待 150ms.
超时时间间隔会越来越长 (不一定是线性增长,具体怎么增长法,取决于系统的具体实现)换而言之,重传的频率越来越低~~

在超时重传中,如果网络上确实出现严重故障了,重传若干次,还是不成功,达到一定次数阈值,就会尝试“重置连接”

重置连接

触发一个“复位报文”(RST)尝试重置连接,相当于连接从新开始,重置就是通信双方清空之前 tcp 传输过程中的中间状态(比如把接受缓冲区 里的数据啥的都不要了),重写开始传输,如果网络出现严重故障,RST报文也是无法顺利完成的,此时重置也失败,就只能断开连接了。(释放掉, 保存对端信息的数据结构)

超时重传,是确认应答的重要补充,TCP 可靠传输, 全靠确认应答和超时重传这俩机制支撑着。

三.连接管理

这里主要是说建立连接的流程:“三次握手”

断开连接的流程:“四次挥手”

三次握手

建立连接就是通信双方保存对端的信息,具体完成这一过程需要经过三次网络交互

在第一次发送的SYN(同步报文),不携带任何业务数据,载荷部分是空着的,只有TCP报头,这个TCP报头的6个标志位中的SYN这一位为1,6个标志为分别为 URG,ACK,PSH,SYN,FIN,其中有些标志位已经介绍到了,有些在接下来的内容中将会介绍

上述图片的流程位,
客户端和服务器各自给对方发送 syn, 再各自给对方返回一个 ack一共, 其实是 4 次交互~~
关键在于, 中间的两次交互 ACK 和 SYN 可以合并成一个网络数据!!
ACK:6 个标志位 第二位 为 1
SYN:6 个标志位 第五位 为 1
所谓的合并,就是让这一个 TCP 数据报,报头中,同时把两个bit都设为 1

三次握手的时候,相当于双方各自让对方保存自己的信息,得是两边都把对方的信息保存好,连接才算是建立完成~~

第一次握手让服务器知道了客户端的发送能力正常,第二次握手让客户端知道服务器的发送功能和接受功能正常,第三次握手让服务器知道客户端的接收功能正常

三次握手主要的意义三个方面:
1.投石问路, 确认通信路径是畅通的。

2.验证通信双方的发送能力和接收能力

3.协商必要的参数,比如 起始序号...

在三次握手中还要服务器和客户端的一些状态需要注意

listen: 是服务器出现的状态.当服务器绑定端口成功之后,就会进入到 listen 状态.
此时意味着,就随时可以有客户端连接上来了,(手机开机,信号良好,就可以随时有人打电话过来了)

established:建立完成,可以随时进行后续的通信了

四次挥手

在上述过程中,四次挥手中间两次是不能合并的,三次握手过程中,,syn 和 ack 都是内核自动控制发送的。(发送时机其实是内核控制的, 同一时机)

但是在四次挥手中ACK(应答报文)是收到FIN(结束报文)就立即返回是内核控制的,而发送FIN(结束报文)是应用程序代码中,调用 close 的时候,才会触发的(应用程序控制的)

在特殊情况下,上述两个数据可以合并,TCP 中还有一个机制,延时应答,(要回复 ack,但不是马上,而是稍等一会..)

四次挥手中也有一些状态需要注意

CLOSE_WAIT:被动方进入的状态,等待代码调用close,代码里调用 close 越及时,这里的状态就越不容易看到。

有的时候,可能会在服务器这边见到大量的 CLOSE WAIT,这说明代码大概率有 bug 了,代码很可能忘记调用 close 了

四.滑动窗口

TCP 除了保证可靠传输之外,也希望能够尽可能高效的完成数据传输滑动窗口, 就是一种,提高传输效率的机制~~

不引入滑动窗口,数据传输过程。A 这边每次收到一个 ACK才发送下一个数据~~
上述过程比较低效~~

引入滑动窗口之后的效果~~
把一条一条发送 =>批量发送。批量发送,就把等待时间重叠了,虽然是批量发送,还是要等待一会 ack。(也不能完全不等,一直往后发,可靠性就难以保证)

滑动窗口中,批量发了 4 组数据之后,不是等到 4 个 ack 都回来才继续发送新的数据而是收到一个 ack,就往后发一个新的~~

在上述滑动窗口中,如果出现丢包了咋办?

加入在批量发送的数据包,序号为1001-5000中,如果2001-3000的数据丢了,在接下来服务器就会不断的给客户端返回3001的ACK,虽然客户端 仍然在继续给 服务器 往后发送接下来的每个数据,但客户端 都是再向服务器索要 3001之前的数据,在客户端连续收到若干个3001这样的ACK,就会明白,原来1来2001-3000这个数据丢了,一旦缺口补充上,接下来就可以从队列中最后一个数据的序号继续往后索要。

重要结论:
滑动窗口,,说是提升效率的机制, 更准确的说,是"亡羊补牢"的机制~~,TCP 为了保证可靠性,牺牲了很多效率的,引入滑动窗口,是让效率上的牺牲,变少一些,但是仍然是存在牺牲的。再如何滑动窗口,速度不可能比 UDP 这种没有可靠机制的协议更快~~

五.流量控制

滑动窗口,涉及到关键概念,窗口大小,批量发送多少数据,数据量,就称为"窗口大小”

窗口大小是可变的,可以通过窗口大小,来控制发送方的发送速度,
窗口越大,单位时间发的数据就越多,效率就越高。窗口越小,单位时间发的数据就越少。

通常情况下,肯定是希望,尽可能高效的传输。但是高效前提一定是可靠性,如果发送速度太快,接收方处理不过来,此时还是麻烦事 (还是可能会引起丢包)

跟合理的做法是,接收方告诉发送方,你发的太快了,我处理不过来,这样的机制就成为“流量控制”,接收方,根据自身的处理能力,反向制约发送方的速度,使双方达成一个"平衡”

具体如何,衡量接收方的处理速度呢?

接受方,有一个接受缓冲区(阻塞队列)

以空闲空间大小作为发送方发送数据的窗口大小,把这个数值,告诉发送方,接收方会给发送方返回 ack。在 ack 报文中,在 tcp 报头里,指定一个字段,表示上述的空闲空间大小~~

发送方就可以按照上述窗口大小,决定下一轮数据发送的窗口大小了

六.拥塞控制

和流量控制类似,都是和滑动窗口搭配的机制,流量控制是,站在接收方的角度,影响发送方的速度

链路上的任何一个节点,性能瓶颈都会制约发送方的发送速度!

考虑中间节点,就复杂了,中间有多少设备? 每次走的路径,都可能不一样,每个设备处理能力,繁忙程度,都不一样.

所以我们将中间的节点看成一个整体,任凭你中间结构多复杂,tcp 都把他们视为一个整体...然后通过"实验"的方式, 找到一个合适的窗口大小(发送速度)

刚开始,按照小的速度,小的窗口来发送数据,如果没有出现丢包(说明中间链路非常畅通)就可以增加速度,增加窗口大小。
如果还是没丢包,仍然很畅通,中间路径都是可以抗住的~~继续增加速度,继续增加窗口大小~~

增加到一定程度,发送速度非常快了,此时可能某个设备达到瓶颈,出现丢包了,此时, 发送方立即减小窗口大小,继续发送, 看是否还丢包.如果不丢包,再继续尝试加,如果丢包, 就继续尝试减

这样就能找到一个合适的窗口大小的值,就可以不丢包,并且还能以比较快的速度完成传输

在流量控制和拥塞控制都印象发送速度,采用那个捏,其实很简单,可以参考木桶效应,那个窗口小就听那个

拥塞控制窗口大小的规律

1.刚开始以, 比较小的窗口 来传输数据~~

2.按照指数方式扩大窗口(*2)

3.指数增长过程中,达到某个阈值,就要变成线性增长.(+n),增长速度减慢

4.增长到一定程度,就会出现丢包,此时,发送方大概就摸到了 网络 大概的能力是在啥样的水平,
此时就会立即把窗口变小(发送速度减下去

5.缩小有两种方式:1)直接缩到底,回到了最初慢启动的时候,接下来指数增长-线性增长
                                2)缩到 出现丢包时 窗口大小 一半 这样的位置接下来线性增长

七.延时应答

ack 不会立即返回,而是稍等一会再返回。

为啥要延时?? 核心目的,提升传输的效率。决定传输速度最关键的因素是窗口大小。
在能够承受的前提下,尽可能的提高窗口大小,通过延时,就可以使窗口大小得到提升~~
延时,就是给应用程序,腾出来更多的消费时间~

举例:发送方A给接收方B发了一个1KB的数据,而B的缓冲区有5KB,如果立即返回ACK,ACK中的窗口大小就是4KB,如果延时一会再返回 ack ?? 在延时的时间里,应用程序和可能就把刚才的数据消费了一部分,(应用程序读了 512 个字节的数据)接收缓冲区,还剩下 512 字节了,此时返回的窗口大小,4.5kb

八.捎带应答

建立再延时应答的基础之上,提升效率的机制,日常开发中,客户端服务器之间的通信,经常是"一问一答" 这样的模型,正常来说, ack 是内核收到请求,自动返回的。由于延时应答的存在, ack 不一定立即返回,在 ack 稍等这一会的时候,正好就要返回响应数据此时,就在响应数据中, tcp 中的 ack 这一位设置上,把 确认序号/窗口大小 都设置上.

这样的效果,也就是为了提高效率~~把两次传输, 合并成了一次传输。

九.面向字节流

在字节流读写数据的场景中,会涉及到一个非常关键的问题,粘包问题~~

举例:假设服务器是一个翻译服务器需要把英文转成中文,客服端连续给服务器发送了“helloworld”,服务器调用 read 来读取请求,由于字节流的特点,读的时候咋读都行,一次可以读一个字节,也可以一次读若干个字节...
此时,服务器无法区分,从哪里到哪里是一个完整的单词~~

为了结局这个问题提出两种方案1.使用分隔符,定义任意字符都可以.只要字符在请求数据中是不存在的

                                                   2.约定包的长度

十.异常情况

1.其中某个进程崩溃了

进程崩溃也好,正常结束也好,操作系统, 都能够回收释放对应的 PCB, 可以释放里面的文件描述符表,也就相当于调用 close.

2.某个主机被关机(正常流程的关机)

对于这种正常流程的关机,操作系统会先尝试强制结束所有的用户进程,然后再进入关机流程~~

这个过程也会和上面一样,结束进程之后,进行四次挥手~~

A 和 B 建立 TCP 连接, A 这边关机了,A 关机之前, 告诉 B, 发送FIN给B,B 这边收到了 FIN, B 返回 ACK, 代码进入下一阶段流程,准备发送 FIN,此时如果 A 已经关机了,意味着 B 接下里的 FIN 就会反复重传几次~~
此时 B 反复重传几次,没有 ACK, 还是会继续吧 A 给删掉了~,A 都已经关机了,之前保存的 B 的消息,自然也没了(内存)

3.某个主机电源掉点了

A 和 B 通信, A 突然掉电了,A 无法做出任何反应就无了。B 还傻傻的以为 A 还存在呢~

这样的分两种情况

1.B 是发送数据方,A掉电了
B 接下来发的数据,都不会有 ack 了.
B 就会触发超时重传,重传几次之后,发送复位报文 (RST).RST 也没有响应,B就会单方面删除保存的 A 的信息

2.B 是接收方
接收方,无法知道对方啥时候给我发数据,当 A 沉默了之后,B 也不知道 A 是暂时暂停一会,还是 A 挂了~~

B 在一定时间之内没收到 A 的数据之后,就会触发心跳包,心跳包就可以认为是一个 没有载荷 的数据包只是为了触发 ack
B 给 A发了一个心跳包,如果 A 正常, A 就会回应 ACK,如果 A 挂了,B 不会收到任何回应,连续发了若干次,A 都没有回应,这个时候 B 就认为 A 挂了,于是单方面释放连接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值