网络原理知识(8)

上次我们讲到了TCP的核心机制,讲到了流量控制与拥塞控制对窗口大小的约束,接下来我们收个尾,将剩下的四个都讲完,这个网络原理的面试部分也就要完结了。

核心机制七:延时应答

在默认情况下,接收方都是在收到数据包的第一时间就返回ack的,但是可以通过延时返回ack的方式来提高效率。

之前我们学习了流量控制,接收缓冲区就是最大容量减去水位的剩余空间,可是此时我们不立即返回,而是等一会再返回。

在等一会的过程中,应用程序就会消耗掉缓冲区中的一部分数据了,于是窗口变大了,此时再返回,也就提高了效率。

可是,这个一定能提高效率吗?

其实不是的,理论上不是100%提高效率,从经验上来看是有帮助的,这里还是要看应用程序消费的速度快慢,如果在延时期间内,接收方又收到了其他的数据,那么窗口就变小了,效率也随之降低。

于是延时应答目的是在应用程序能够处理的限度下,尽可能的增加窗口大小。

那么所有的包都可以延时应答吗?也不是,因为他有两方面的限制:

(1)数量限制:每隔n个包就应答一次(n是自己设置的,这样就不会因为ack少了就影响可靠性,确认序号,后一个将前面的覆盖)

(2)时间限制:超过最大延迟时间就应答一次

这两个方面,如果传输的数据密集,就按照第一个;数据稀疏,就按照第二个来。

核心机制八:捎带应答

这个是在延时应答的基础上进行的,在返回业务数据的时候,顺便把上次的ack给带回去,于是在下图中的ack与响应两次就合并为一次,也就是说,仅在这种情况下,四次挥手中间的两次是可以合并的(之前的面试题)。

如果没有延时应答,返回ack与返回响应的时机一定是不同的,而引入之后,ack可以往后延时一定时间,恰好这个时候要返回响应数据,此时就可以把ack也代入到响应数据中一起返回。

把两个包合成为一个,就能起到提高效率的作用。

核心机制九:面向字节流

粘包问题

粘包,粘的是"应用层数据包",通过字节流方式传输,很容易混淆包和包之间的边界,从而接收方无法区分从哪里到哪里是一个完整的应用层数据包。

此时的接收缓冲区:

此时,接收方在从接收缓冲区中read数据的时候,由于他们粘在一起了,就不知道要读几个字节了,如果包读的不完整或读多了,都可能使程序出现bug(字节流传输太灵活了)。

对于UDP来说,就不会出现这样的问题,UDP是以数据包为单位读写的(面向数据报),一个UDP数据包承载一个应用层数据包,每次receive得到的结果就是一个完整的应用层数据包。

那么该怎么解决呢?

上述问题,在TCP的层次上是无解的,需要站在应用层上解决,那么就要定义好应用层协议,明确包与包之间的边界。

如下面两个方案:

(1)约定包与包之间的分隔符,作为一个包的结束标记。这是echo server采用的办法,约定\n作为结束标记,之前写的TCP服务器代码那里也有,就是hasNext这样的。

(2)约定包的长度,比如约定每个包开头4个字节,表示数据包一共多长。

先读4个字节得到长度,再根据长度的值,决定接下来读多少,比如,先读3,再往后读三个值,也就是"aaa"。即使是"43aaa"(其中3是数据包中的数据,4只是个报头),也可以像上图一样精准的表示出来,便于读取。

HTTP中两种方案都有体现:

1.get请求,没有body,就使用空行作为结束标记

2.post请求,有body时,通过Content-Length决定body多长

核心机制十:异常情况的处理

TCP在通信过程中存在以下四种特殊情况(异常):

1.某个进程崩溃了

进程崩溃和主动退出没有本质区别,进程释放时,返回文件描述符,此时表中的每个资源都调用socket的close。

调用close时,也就开始了第一个fin,触发了四次挥手,进程虽然没了,但由于刚开始四次挥手,TCP的连接信息还存在(TCP连接释放时机更晚),此时四次挥手还是可以正常进行的。

2.主机关机了

正常流程的关机,本质上是会杀死所有的用户进程,这样就和第一种情况一样了。

当然,关机也需要一定的时间,如果在一定时间内四次挥手进行完毕了,就和正常的情况一样了,但如果关机之后仍然没有挥手完呢?

假设在A关机之后,B的fin才来到A这里,意味着B的fin不会有A的ack返回,此时B重传fin,也没有ack,经过几次超时重传之后,B就可以认为对端出现严重问题,于是B主动放弃连接(B把保存的关于A的信息就删掉了),最后B仍然可以把连接释放掉。

3.主机掉电了

台式机中,拔掉了电源,就会出现这种情况,当然这样是很伤硬盘的,不要随便拔。

这里分两种情况(全是A掉电):

(1)接收方掉电

这里在B给A发送了数据包之后,A突然掉电了,B后续发来的数据就没有ack返回了,B一开始超时重传,重传达到了一定次数仍没有ack,就会触发"重置TCP连接",B主动发一个复位报文,之前的就都从头开始了,如果rst同样没有ack,那么B就只能单方面释放连接了。

(2)发送方掉电

B作为接收方发现,A突然没有声音了,此时作为接收方的B区分不了A是挂了还是暂时休息了一会,B就只能继续等待,B等待一定时间后,就会周期性的给A传输一个特殊的报文,"心跳包",这个不携带业务数据(载荷),只是为了触发ack,如果对方有心跳,那就继续正常等待,如果没有心跳,就只能通过rst尝试,若还是不行,就只能单方面释放连接。

这就是"保活机制",在分布式系统中,心跳包的思想方法,是非常广泛使用的(但TCP的心跳,周期太长了,是分钟级别的)。

虽然TCP内置了心跳包,在实际开发中,通常还是会在应用层重新实现心跳包的效果。现在通常希望是在秒级甚至毫秒级就能发现对端是否正常存活,从而触发一些后续的操作。

比如一共有10000条请求来到了入口服务器,为了提高处理的效率,入口服务器通常会将他们分给若干个业务服务器中,一起来处理这些请求。

4.网线断开了

相关推荐
被摘下的星星2 小时前
路由是什么
网络·智能路由器
我命由我123452 小时前
Android 广播 - 显式广播与隐式广播
android·java·开发语言·java-ee·kotlin·android studio·android-studio
SimonKing2 小时前
frontend-dev vs ui-ux-pro-max:谁才是Vibe Coding前端开发的“最强辅助”?
java·后端·程序员
小谢小哥2 小时前
57-数据同步方案详解
java·后端·架构
小谢小哥2 小时前
56-最终一致性方案详解
java·后端·架构
人道领域2 小时前
【Redis实战篇 | Day04】Lua原子性优化Redis分布式锁:解决线程安全问题
java·开发语言·redis·性能优化
V我五十买鸡腿2 小时前
安全第一课
网络·安全·web安全
恋奴娇2 小时前
ubuntu 25 Nautilus 文件管理器不能以ROOT运行突破
java·数据库·ubuntu
明天有专业课2 小时前
RAG-向量数据库Milvus
面试·aigc