JavaEE初阶9.0

目录

传输层协议

一、传输层协议

[1.0 UDP](#1.0 UDP)

(1)简介

(2)UDP协议报文格式

(3)校验和

[2.0 TCP/IP](#2.0 TCP/IP)

(1)简介

(2)TCP协议报文格式

[(3)核心机制1 确认应答](#(3)核心机制1 确认应答)

[(4)核心机制2 超时重传](#(4)核心机制2 超时重传)

(5)核心机制3:连接管理

[(6)核心特性4 滑动窗口](#(6)核心特性4 滑动窗口)

[(7)核心特性5 流量控制](#(7)核心特性5 流量控制)

[(8)核心特性6 拥塞控制](#(8)核心特性6 拥塞控制)

[(9)核心机制7 延时应答](#(9)核心机制7 延时应答)

(10)核心机制8:捎带应答

[(11)核心机制9 面向字节流](#(11)核心机制9 面向字节流)

[(12)核心机制10 异常情况处理](#(12)核心机制10 异常情况处理)


传输层协议

一、传输层协议

1.0 UDP
(1)简介

以后写代码的时候 都是要调用传输层提供的api接口的

应用层很多操作 都是要调用传输层提供的api接口的( socket api(网络套接字))

(2)UDP协议报文格式

HTTP的报头格式是文本格式的 UDP/TCP/IP报头是二进制的 端口号的范围1024->65535

长度属性 也是2个字节 表示范围是0~65535 也就是64kb

那么如何传送一个大的数据呢 ? 应用层代码拆包操作 或者 使用TCP协议 没有数据包长度的限制

为什么不把长度扩大到4个字节呢? 不是技术问题 更重要的是zz问题 UDP这样的协议 已经被内置到各种操作系统中了 一旦一方改了 另一方不改 这样就会导致两个设备无法UDP通信了 谁先改谁就导致无法访问没改的设备 这样只会把锅怪到windows上面 windows当然不认 所以udp只能这个样子 (升级类工作 一定要考虑到兼容性的问题~)

(3)校验和

验证数据是否发生修改的手段

HTTPS的数字签名 为了防止黑客篡改(防人)

UDP的校验和 不是为了防人 和安全性无关 而是为了防止传输过程中的"比特翻转" 1变0 0变1

光信号 电磁波 电信号等等 受到外界的干扰 可能会是信号发生改变

发送之前 先计算一个校验和 把整个数据包的数据都代入 把数据和校验和一起发送给对端

接收方受到之后 重新计算一下校验和 和收到的校验和进行对比(UDP发现校验和不一致 就会直接丢弃) 使用的是CRCC方式( 循环冗余检验)

把每个字节都当作整数 进行累加 溢出也没有关系 继续加最终得到结果 传输到对端 数据出现错误了 对端再次计算校验和 就会和第一个检验和不一样了

2.0 TCP/IP
(1)简介
(2)TCP协议报文格式

这个选项使得tcp的报头长度是可变的(选项可以有一个 可以有多个)

固定部分是20字节了 再算上选项肯定比15要多啊 这里不是使用字节为单位 而是使用4个字节为单位

UDP的问题是长度不够 又不能扩展~ TCP的设计者考虑了这个问题 它预留了一些保留位 保留了一些核心内容

(3)核心机制1 确认应答

可靠性:100%做不到 A给B发了消息之后 尽可能的让B收到

A能够知道B是否收到了

确认应答:对方返回一个"应答报文" 发送方收到应答报文 就可以确认对方是收到了(当年发短信非常容易丢了~~)

产生后发先至的问题 消息延迟 我发了拒绝了 然后后悔了 发了改成同意吧 结果对面先接收到同意的消息 这个咋整嘞

为什么网络上会出现后发先至?

结婚的时候 接亲的车队中可能有一辆车比头车先到女方家(因为路况 红绿灯 意外 路线等等)

网络上 转发数据 每个路由器/交换机 就相当于是十字路口

TCP的处理方案是 给传输的数据 进行编号 第一条消息编号为1 上面图片里面的32位序号 32号确认序号 就是用来处理这个问题的 ack为1 表示是应答报文

TCP是面向字节流的 其实在编号的时候 不是按照1条 2条这样的方式 而是用字节来编号的

填写法是:把收到的数据载荷部分的最后一个字节序号+1 填写到确认序号中

引入序号之后 接收方就可以根据序号对数据进行排序~ TCP需要处理这样后发先至的情况 确保应用程序通过socket api读到的数据顺序是正确的~ // 缓冲区啥的(先到的车在村口等新郎的头车 这里面在涉及到的在村口排队的操作就是缓冲区啥的 )

(4)核心机制2 超时重传

针对丢包情况做出处理:

为啥会丢包呢:

网络结构非常复杂 我们的数据包经过某个路由器/交换机已经非常繁忙了 导致当前转发需要转发的数据量超出路由器/交换机的转发能力上限 (路口堵车)(不可避免地丢包)

假设丢包的概率是10% 连续两个包 至少有一个达到的概率是95%

随着重传次数的增加 数据报到达对方的概率增加~

引入超时时间来判定是否丢包:

TCP中判定超时的阈值 是动态改变的(会延长这个时间阈值 随着重传 概率高了 重传还不成 说明我们增加了概率还不成功 意味着当前丢包概率是一个非常大的数值 意味着网络大概率已经出现严重故障了) 哈哈类似医院抢救病人 多多电击几次

A---->B B------>A 过程中发生丢包 A->B 发的数据丢了 B->A发的数据丢了

第二种情况:相当于B接收了两次A发送的请求呢 收到了两份同样的数据 万一是扣款呢 这个时候之前了解的缓冲机制就发力了 根据序号去重

综上:保证TCP可靠传输的机制是 确认应答+超时重传

(5)核心机制3:连接管理

主要是建立连接和断开连接

连接:网路上的连接是抽象的 通信双方 各自保存对端的信息

建立连接:

TCP通过三次握手的方式来完成 发送一个不携带业务的数据 通过这个数据和对方"打个招呼"

中间那两个可以进行合并 所以叫做 三次握手

同步:在TCP中的同步 指的是"数据上的同步" A告诉B 接下来 我要和你建立连接 据需要你把我的信息保存好 同时你也把你的信息同步发过来 同步报文表示状态 并且各自给对方返回一个ack告知自己收到了~

主动的一方叫做客户端 这里一定是客户端主动发起syn 客户端和服务器不是固定的 只是一种关系

为什么TCP要三次握手,有啥用,解决了啥问题:

1.三次握手 相当于投石问路 先初步探一探网络的通信链路是否通畅 (地铁早上的第一班车是不载客 是为了验证的路线是畅通的~)

2.验证通信双方的发送能力和接收能力是否也正常~~(先打开QQ语音 看看有故障没有)

3.三次握手过程中,可以协商一些关键信息~~ TCP要协商的一个非常关键的信息 通信过程中 序号从几开始 初始序号 一般不是从0开始的~ 并且 两次连接初始序号都是不同的

断开连接:

四次挥手

FIN:Finish 结束报文 ACK:Acknowledgement的缩写 代表"确认" 用于确认已成功接收对方之前发送的报文

为什么不能合并是因为ACK和FIN ?

这两次交互的时机是不同的 如能 有时候能有时候不能

ACK是内核控制返回的 内核收到FIN 第一时间返回ack 和你应用程序的代码无关

FIN则是代码中调用socket.close才会触发的(进程结束 也能触发)

第二个FIN的时机和ACK的时机很可能不是同一时机~

TIME_WAIT

TIME_WAIT 是TCP连接关闭后 主动关闭方会进入的状态 核心作用是确保连接正常终止 避免旧报文干扰新连接 默认持续是2MSL(报文最大生存时间 通常是1--4min)

看四次挥手的图:A发送finish B收到之后发送ack(确认了 我收到了)和fin 然后A接收到之后返回ack表示我也确认收到了

假设出现这样情况:如果B给A发FIN A收到了 A收到之后 返回ACK把连接释放了 但是这个返回的ACK万一丢包了呢 没有给B发送过去 这个时候B的FIN刚好重传 可是A已经把连接释放了 这个时候B怎么把它的FIN发送过去呢? 就是下面的这个情况:

现实例子就是:我去送妹妹上学 但是我不知道学校今天是否开着 我带着妹妹到了学校大门口妹妹进去 到了教室门口看一下 如果教室有人 就进去上课 如果教室没人 就回来 在她从学校门口到到教室门口的时候 我在外面等着 等待最大时间为妹妹从学校大门口到教室门口的距离所花费的时间X2 如果超时了妹妹还没回来 推断出妹妹应该是上课了 我就回去了

CLOSE_WAIT

被动方已收到对方的FIN报文 但是自身还没有调用close()关闭连接

这个地方在等待你的应用程序代码 来调用close方法 我们在写服务器代码的时候

原则上来说 感知到对方断开之后 就应该尽快的执行close~

如果你发现服务器这边存在大量的CLOSE_WAIT 持续的还很久 此时意味着你的代码大概率有bug

(6)核心特性4 滑动窗口

滑动窗口: 窗口是指发送方在未接收到接收方确认的情况下 最多能连续发送的数据字节数 窗口的大小由接收方通过TCP报文头部的窗口字段告知发送方 因此窗口的大小是动态变化的

TCP协议保证可靠性的同时也是要付出代价的

窗口越大 批发量的数据越多 效率也越高 但是窗口也不能无限大 太大也会影响到可靠性

滑动窗口是在可靠传输的基础上 提高效率 这样只是亡羊补牢 引入可靠性 会使效率产生折损 引入滑动窗口 是要让折损更小 这种方式可能效率比UDP这种还高~~

正常情况下就是单个传送 这样的效率是很低的 可以攒着一块发送

这样就相当于用一份时间等待多组ACK(把多组等待ACK的时间重叠成一份了)

B收到之后要怎么发呢 有两种选择:一种是等这一组的所有ACK都回来 再发第二组 这么做 花费的时间会更长 更好的做法是收到一个ACK 就发下一条

这个过程丢包怎么办:

确认序号的含义:该序号之前的数据,都确认收到了~~~ 它是告知发送方:我们已经正确接收了确认号之前的所有字节 下一步请给我发从这个确认号开始的数据

2001ack能够涵盖1001的含义 这个过程丢包 就像是下面的情况

情况1:数据已经抵达 ACK被丢了

对于这种情况 不用做任何的处理 因为后面一个ACK能够涵盖前一个ACK的含义

快速重传是TCP中加速丢失数据重传的机制

超时重传和快速重传 这两个不是矛盾的机制 即使相当于牛顿的经典力学和相对论之间的关系 两者之间是相辅相成的 一个是宏观世界的规律 一个是微观世界的规律

情况2:数据包直接丢了

这种情况是直接丢包了

B收到之后 仍然索要1001 三次重复的确认应答 让A意识到1001-2000包怕是丢了 A重传1001-2000 B收到1001-2000后 补全空的拼图 至此拼图完整结束

(7)核心特性5 流量控制

滑动窗口 窗口越大 效率就越高 但是不能无限大起来 太大了会影响到可靠性 接收方的处理能力也是有上限的

有一个TCP缓冲区

蓄水的速度 就是发送方的速度 放水的速度 就是应用程序读取的速度

流量控制就是给发送方踩刹车 让它发的慢点

流量控制可以让接收方 根据自身处理数据的速度 反馈给发送方 限制发送方发送的速度

在ACK中 依赖一个特殊的属性 窗口大小

(8)核心特性6 拥塞控制

流量控制是依据接收方处理能力 进行限制的

拥塞控制是依据传输链路的转发能力 进行限制的

抽象一下 发送方到接收方的过程中 需要穿过一张由路由器构成的网络

这个过程中 先按照小的窗口(小的速度) 先发着

如果发的时候很顺利 不丢包 就加大速度 出现丢包就减小速度 又不丢包了 继续加大速度 又丢包了 继续减小速度 慢慢的达成一种动态平衡 这个就是拥塞控制的大体思想

面多加水 水多加面~

呜 这个图就表示了拥塞控制的工作过程 慢启动 指数增长 线性增长 丢包 窗口变成最小值.........

(9)核心机制7 延时应答

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

通过这样 返回一个更大的窗口大小~~

应用程序能够处理的限度下,尽可能的增加窗口的大小(哈哈哈 看到消息之后立马补好作业 做好作业之后 再给老师回消息 理直气壮 利用延时时间赶紧消费队列中的数据)

也不是100%能提高效率 完全有可能在延时期间内 接收方又收到其他数据 导致效率更低了 理论上不是100%提高效率 但是从经验上大概率提高效率

不是所有的包都可以延时应答 数量限制和时间限制 综合来看的 具体参数 隔多少包 隔多少时间 不需要被背 参数通常是可以调整的

(10)核心机制8:捎带应答

我们在返回业务数据的时候 顺便把上次的ack带回去 如果没有延时应答 返回ack的时机和返回响应的时机 就是不同的时机 这两者中间可能干一点时间 我们可以让ack往后面延时一下 恰好这个时候要返回响应数据 此时就可以把ack也代入到响应数据中 一起返回

这样就可以一个数据包 完全两件事 这样把两个包合并成一个 提高了效率 封装分用

(11)核心机制9 面向字节流

粘包问题 粘nian:粘的是"应用层数据包" 通过字节流方式传输,很容易混淆包和包之间的边界 从而接收方无法区分 本来发的是 aaa bbb ccc 但是接收到的是aaabbbccc 怎么拆分呢

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

约定包和包之间的分隔符(aaa\nbbb\n\ccc\n) 这样就比较明确了

约定包的长度:约定每个包开头四个字节就表示这个数据包多长3aaa4bbbb5ccccc

在HTTP中 这两种方式都有体现 GET请求没有body使用空行来作为结束标记 POST请求有body的时候 通过Content-Length决定body多长

成熟的方案 例如json protobuf都已经把粘包问题解决了

(12)核心机制10 异常情况处理

TCP通信过程中存在特殊情况 某个进程崩溃了 主机关机了 主机掉电了 网线掉开了

进程崩溃

和主动退出没有本质区别 进程释放=> 回收文件描述符表的每个资源 => 调用socket的close

FIN 触发四次挥手

进程虽然没了 但是TCP的连接信息还在(内核控制的)

主机关机了

正常流程关机 本质上还是会先杀死所有的用户进程 本质上和进程崩溃是一样的

也会触发四次挥手 正常结束

但是如果没有挥完呢 B尝试重传几次 主动放弃连接 最后B仍然可以把连接释放掉~

主机掉电

拔电源(补药随便拔 可能会出现问题 笔记本有内置电池 台式机这样的情况就不太乐观)

发送方突然掉电了 B突然发现A没有声音了 此时B区分不了A是挂了 还是暂时休息一会 B只能继续等但是不会无限等 B等待一段时间之后 就会给A传输一个特殊的报文"心跳包" 不携带业务数据(载荷) 只是为了触发ACK

虽然TCP内置了心跳包,实际开发中,通常还是会在应用层重新实现心跳包效果

网线断开

站在A的视角就是和我们刚才接收方掉电是一样的情况

站在B的视角 就是和刚才上面发送方掉电是一样的情况

标志位的补充:

URG 紧急指针位 TCP正常来说 是按照序号顺序 发送和接收~

紧急指针相当于"插队" 跳过前面的数据,直接从某个指定的序号来开始

PSH:催促标志位 接收方看到了这个就会尽快的把这个数据read到应用程序中

传输层不只有UDP和TCP两个协议 也有一些专门的协议来应对~ 典型的是KCP(介于UDP和TCP之间的协议 兼顾两个的优点)

相关推荐
爱学习的小可爱卢2 天前
JavaEE进阶——SpringMVC响应处理详解
spring boot·postman·javaee
带刺的坐椅5 天前
Solon 不依赖 Java EE 是其最有价值的设计!
java·spring·web·solon·javaee
想不明白的过度思考者9 天前
基于 Spring Boot 的 Web 三大核心交互案例精讲
前端·spring boot·后端·交互·javaee
朝新_13 天前
【实战】博客系统:项目公共模块 + 博客列表的实现
数据库·笔记·sql·mybatis·交互·javaee
朝新_15 天前
Spring事务和事务传播机制
数据库·后端·sql·spring·javaee
朝新_16 天前
【统一功能处理】SpringBoot 统一功能专题:拦截器、数据封装、异常处理及 DispatcherServlet 源码初探
java·spring boot·后端·spring·javaee
朝新_20 天前
【统一功能处理】从入门到源码:拦截器学习指南(含适配器模式深度解读)
数据库·后端·mybatis·适配器模式·javaee
朝新_1 个月前
【SpringBoot】玩转 Spring Boot 日志:级别划分、持久化、格式配置及 Lombok 简化使用
java·spring boot·笔记·后端·spring·javaee
朝新_1 个月前
【SpringMVC】详解用户登录前后端交互流程:AJAX 异步通信与 Session 机制实战
前端·笔记·spring·ajax·交互·javaee