当前位置: 首页 > article >正文

Java 网络八股(2) TCP6大核心机制/异常处理

1. 滑动窗口

假设1-1000发送一次

1001-2000发送一次

2001-3000发送一次

...

可以合并成一次

1-1000发送之后

立即发送1001-2000发送之后

立即发送2001-3000,不需要等待ACK

收到第一个1001的ACK之后,发送

3001-4000的数据以此类推

如果出现丢包,如何进⾏重传?这⾥分两种情况讨论.

情况⼀:数据包已经抵达,ACK被丢了.

这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进⾏确认;

情况⼆:数据包就直接丢了.

当某⼀段报⽂段丢失之后,发送端会⼀直收到1001这样的ACK,就像是在提醒发送端"我想要的是 1001" ⼀样;

• 如果发送端主机连续三次收到了同样⼀个"1001"这样的应答,就会将对应的数据1001-2000重新 发送;

• 这个时候接收端收到了1001之后,再次返回的ACK就是7001了(因为2001-7000)接收端其实之前就 已经收到了,被放到了接收端操作系统内核的接收缓冲区中; 这种机制被称为"⾼速重发控制"(也叫"快重传").

2. 流量控制

滑动窗口的窗口大小可以无限大吗

接收端处理数据的速度是有限的.如果发送端发的太快,

导致接收端的缓冲区被放满,这个时候如果发送端继续发送,就会造成丢包,继⽽引起丢包重传等等⼀系列连锁反应.

因此TCP⽀持根据接收端的处理能⼒,来决定发送端的发送速度.这个机制就叫做流量控制(Flow Control);

• 接收端将⾃⼰可以接收的缓冲区⼤⼩放⼊TCP⾸部中的"窗⼝⼤⼩"字段,通过ACK端通知发送端;

• 窗⼝⼤⼩字段越⼤,说明⽹络的吞吐量越⾼;

• 接收端⼀旦发现⾃⼰的缓冲区快满了,就会将窗⼝⼤⼩设置成⼀个更⼩的值通知给发送端;

• 发送端接受到这个窗⼝之后,就会减慢⾃⼰的发送速度;

• 如果接收端缓冲区满了,就会将窗⼝置为0; 这时发送⽅不再发送数据,但是需要定期发送⼀个窗口探测数据段,使接收端把窗⼝⼤⼩告诉发送端.

3. 拥塞控制

流量控制站在接收方视角限制发送方速度

拥塞控制站在传输链路的视角限制发送速度

如果发送端发的太快,

可能导致中间路由或者交换机的缓冲区被放满

TCP引⼊慢启动机制,先发少量的数据,探探路,摸清当前的⽹络拥堵状态,再决定按照多⼤的速度传输 数据

为了不增⻓的那么快,因此不能使拥塞窗⼝单纯的加倍.

• 此处引⼊⼀个叫做慢启动的阈值

• 当拥塞窗⼝超过这个阈值的时候,不再按照指数⽅式增⻓,⽽是按照线性⽅式增⻓

• 当TCP开始启动的时候,慢启动阈值等于窗⼝最⼤值;

• 在每次超时重发的时候,慢启动阈值会变成原来的⼀半,同时拥塞窗⼝置回1; 少量的丢包,我们仅仅是触发超时重传;⼤量的丢包,我们就认为⽹络拥塞;

4. 延时应答

如果接收数据的主机⽴刻返回ACK应答,这时候返回的窗⼝可能⽐较⼩.

• 假设接收端缓冲区为1M.⼀次收到了500K的数据;如果⽴刻应答,返回的窗⼝就是500K;

• 但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了;

• 在这种情况下,接收端处理还远没有达到⾃⼰的极限,即使窗⼝再放⼤⼀些,也能处理过来;

• 如果接收端稍微等⼀会再应答,⽐如等待200ms再应答,那么这个时候返回的窗⼝⼤⼩就是1M; ⼀定要记得,窗⼝越⼤,⽹络吞吐量就越⼤,传输效率就越⾼.我们的⽬标是在保证⽹络不拥塞的情况下 尽量提⾼传输效率; 那么所有的包都可以延迟应答么?肯定也不是;

• 数量限制:每隔N个包就应答⼀次;

• 时间限制:超过最⼤延迟时间就应答⼀次; 具体的数量和超时时间,依操作系统不同也有差异;⼀般N取2,超时时间取200ms;

 

5. 捎带应答

在延迟应答的基础上,我们发现,很多情况下,客⼾端服务器在应⽤层也是"⼀发⼀收"的.意味着客⼾端 给服务器说了"Howareyou",服务器也会给客⼾端回⼀个"Fine,thankyou"; 那么这个时候ACK就可以搭顺⻛⻋,和服务器回应的"Fine,thankyou"⼀起回给客⼾端\

6. 面向字节流(粘包问题)

⾸先要明确,粘包问题中的"包",是指的应⽤层的数据包.

• 在TCP的协议头中,没有如同UDP⼀样的"报⽂⻓度"这样的字段,但是有⼀个序号这样的字段.

• 站在传输层的⻆度,TCP是⼀个⼀个报⽂过来的.按照序号排好序放在缓冲区中.

• 站在应⽤层的⻆度,看到的只是⼀串连续的字节数据.

• 那么应⽤程序看到了这么⼀连串的字节数据,就不知道从哪个部分开始到哪个部分,是⼀个完整的应 ⽤层数据包. 那么如何避免粘包问题呢?归根结底就是⼀句话,明确两个包之间的边界

两个办法

• 对于定⻓的包,保证每次都按固定⼤⼩读取

• 对于变⻓的包,可以在包头的位置,约定⼀个包总⻓度的字段,从⽽就知道了包的结束位置

7. 异常处理

进程崩溃/主机关机 相关的都没事,一个是操作系统层面,socket会自动关闭->双方释放

停电/断网 接受方出现故障就是发送方发送ACK确定对方是否活着->单方面释放

发送方出现故障就是接收方发送周期的心跳包-> 单方面释放

  1. 进程崩溃
    1. 在java中就是抛出异常,但是没人catch,最终异常到了jvm,jvm进程就直接崩溃了
    2. 进程崩溃时,PCB就被回收,PCB中的所有文件都会被系统自动关闭
    3. 其中socket,也会出发正常的关闭流程(TCP四次挥手)
  2. 主机关机
    1. 正常流程中点击关机,此时操作系统会干掉所有的进程
    2. 也会触发正常的关闭流程(TCP四次挥手)
  3. 主机掉电(拔电源)
    1. 如果是接收方掉电
      1. 超时重传
      2. TCP正常断开,双方各自删除对面
      3. TCP异常断开,只能删除自己的,不管对面了
    2. 如果是发送方掉电
      1. 接收方会发送周期的心跳包->确定对方是否还活着
  4. 网线断开

        和主机掉电是一样的

UDP实现可靠传输(经典面试题)

就是要讲TCP的特性

编写应用层的代码的时候,要引入确认应答,接收方返回ACK

防止粘包问题,可以考虑使用字符串截断或者考虑定长包

提高效率,使用滑动窗口,快速重传

保证滑动窗口效率,使用流量控制和拥塞控制


http://www.kler.cn/a/568543.html

相关文章:

  • 基于单片机的智能宿舍管理系统(论文+源码)
  • 【3天快速入门WPF】11-附加属性
  • 【MongoDB】在Windows11下安装与使用
  • 蓝桥杯web第三天
  • h5 IOS端渐变的兼容问题 渐变实现弧形效果
  • Ubuntu 下 nginx-1.24.0 源码分析 - ngx_init_cycle 函数 - 详解(9)
  • LeetCode 2353. 设计食物评分系统题解
  • Qt 的 Lambda 捕获局部变量导致 UI 更新异常的分析与解决
  • Solar2月应急响应公益月赛
  • 虚拟机中的指示命令
  • 使用SPI总线与外部传感器通信,使用ECU抽象
  • rust学习笔记9-结构体与206.反转链表
  • 知识图谱neo4j+vue+flask课程在线学习系统
  • 聊一聊 IM 如何优化缓存
  • WSBDF レクチア 定义2 引理3 wsbdf的乘子
  • cellphonedb v5受配体多组比较气泡图(原创函数)
  • Visual Studio Code集成MarsCode AI
  • DeepSeek on AWS:解锁高效AI训练与部署的云端密码
  • rust基础-宏与方法之间的区别
  • MongoDB Compass中MONGOSH常用查询整理