【JaveEE】UDP 与 TCP 原理

这篇博客真的很详细很详细很详细,不打算试试看吗 >o

【JaveEE】UDP 与 TCP 原理_第1张图片

文章目录

  • JaveEE & UDP 与 TCP 原理
    • 1. 应用层协议(自定义组织格式)
    • 2. 传输层UDP协议
      • 2.1 数据报报文格式
        • 2.1.1 源端口与目的端口
        • 2.1.2 报文长度和校验和
    • 3. 传输层TCP协议
      • 3.1 TCP是如何保证可靠传输 --- ==确认应答==
      • 3.2 应答报文ACK的作用
        • 3.2.1 丢包
        • 3.2.1 处理丢包现象 --- ==超时重传==
      • 3.3 连接管理
        • 3.3.1 TCP建立连接 --- 三次握手
        • 3.2.2 报文中特殊的六个比特位
        • 3.3.3 TCP断开连接 --- 四次挥手
      • 3.4 TCP是如何挽救效率的
        • 3.4.1 批量发送 --- ==滑动窗口==
        • 3.4.2 流量控制
        • 3.4.3 拥塞控制
        • 3.4.4 延时应答
        • 3.4.5 捎带应答
      • 3.5 面向字节流
      • 3.6 异常情况
        • 3.6.1 进程关闭 / 进程崩溃
        • 3.6.2 主机关机(正常流程关机)
        • 3.6.3 主机掉电(因为电的原因强制快速关机)
        • 3.6.4 网线断开
    • 4. TCP十大核心机制小节
    • 5. TCP 与 UDP的差别

JaveEE & UDP 与 TCP 原理

1. 应用层协议(自定义组织格式)

  • 对应后面的一个章节HTTP协议,应用层的代表协议,到时候重点讲解

而大多数时候,都是程序员自定义组织的协议~

【JaveEE】UDP 与 TCP 原理_第2张图片

  • 而字段与字段之间如何组织呢?

约定:

  1. 传输哪些数据
    • 按甲方爸爸的需求以及实际需要的
  2. 组织格式
    • 随意约定

常见组织格式:

  1. 分隔符
    • 以空格、回车…分割
    • 以分号…表示结束

实际开发会使用一些现成的格式:

  1. xml(基本认识)
    • html是特殊的xml格式
    • html标签名和含义都是固定的,是我们需要遵守的

标签的形式:< XXX > …

【JaveEE】UDP 与 TCP 原理_第3张图片

  1. json(基本认识)
  • 以{ }作为标识

  • 内部有多个键值对,每个键值对之间用逗号分割

  • key与值之间使用冒号分割

    • key必须是字符串,
    • 值可以是数组,字符串,数字甚至是另一个json

【JaveEE】UDP 与 TCP 原理_第4张图片

2. 传输层UDP协议

  • 柿子要挑软的捏~

2.1 数据报报文格式

【JaveEE】UDP 与 TCP 原理_第5张图片

  • 这是教科书上的画法~

其实并不准确,真实的报文应该是这样的:

2.1.1 源端口与目的端口

【JaveEE】UDP 与 TCP 原理_第6张图片

【JaveEE】UDP 与 TCP 原理_第7张图片

可能有人会想,服务器不是远在天边的“服务我们的机器”吗?

  • 然而并不绝对,一些服务器可以是我们本地主机上的一个程序
    • 例如http或者mysql

http有个专属“座位“:80

  • ssh:22
  • ftp:21
  • “端口0”并不正式存在。 它被定义为无效的端口号。

并不是说这些端口号就代表了“远方服务器”的位置!

  • 端口号:主机上程序的位置!

至于“专属座位”有什么用

  • 服务器一般端口号不需要怎么改变,所以保持在一个端口号是个正常的抉择
  • 不然每次你都要去查这个常用的服务器端口号是多少~

2.1.2 报文长度和校验和

【JaveEE】UDP 与 TCP 原理_第8张图片

报文长度:0~65535 => 64KB(64KB - 1B,近似64KB)

  • 代表UDP正文最大就是64KB大而已
  • 不是说只能传一个“65535”大小的整数~
  • 而是65535字节的数据量!

时代不同,现在64KB很小了,一个表情包都能几MB了~

  • 但是如果要扩大这个限制,那么我们就要升级UDP系统
  • 只有都升级,才能通讯,这不现实~

要传输大数据

  1. 把数据拆分为多个数据,用多个UDP数据报传输~
  2. 不用UDP,用TCP去传输 > v o
    • TCP就没有限制

所以,UDP的数据报不宜过长,否则会出错

校验和

  • 我们传递的数据本质是01…,而我们用高低电平表示01

【JaveEE】UDP 与 TCP 原理_第9张图片

  • 难免会有一些干扰,例如强磁场(太阳黑子)
    • 这样就会导致一些高电平转变为低电平,低电平转变为高电平

【JaveEE】UDP 与 TCP 原理_第10张图片

  • 那么校验和的存在,就相当于定下了一个标准
    • 满足这个标准,数据不一定对;但是不满足这个标准,这个数据肯定错
  • 而这个校验和,要是中间数据发生01转变,大概率是对不上的
    • 可能校验和跟数据发生突变后,还对上了。很极端的情况
  • 例如:
    • 小马去太空,原本五根手指,回来变成四根,”校验和为5根手指“,这样就对不上了~
    • 回来还是五根,但是细节不对,但是的但是蒙混过关了~
    • 校验和突变为“4根手指”,反而对上了(极端)

校验和是通过复杂计算后的一个数值~

  • 发送方:

【JaveEE】UDP 与 TCP 原理_第11张图片

  • 接收方:

【JaveEE】UDP 与 TCP 原理_第12张图片

  • 有很多种方式,例如CRC算法,奇偶校验…
  • 对不上就数据就一定不正确!

通过上述结构,也可以看出UDP并不在意数据传输是否成功~

  • 但是却有校验和校验数据正确性 > v <

3. 传输层TCP协议

核心机制,在于接收方收到了或者没有收到,都会有个应答,对传输失败有所动作

  • 教人知识也是如此,我如果教一个同学一个知识,他没有如何应答,我就没法判断他掌握了没有~
    • 会了:我继续讲~
    • 不会:我重新讲~

数据报报文结构:

【JaveEE】UDP 与 TCP 原理_第13张图片

3.1 TCP是如何保证可靠传输 — 确认应答

可靠性不等于安全性

  • 安全性是指容不容易被黑客篡改 / 窃取
  • 可靠性是指数据传输是否能正确到达对方
    【JaveEE】UDP 与 TCP 原理_第14张图片

应答报文规则:

  • 单条信息发送:

    • 发送方没收到应答是不会继续发消息的
      【JaveEE】UDP 与 TCP 原理_第15张图片
  • 多条信息发送:

    • socket里有个接受缓冲区,里面就会把序列排好,再被接收方读取~

【JaveEE】UDP 与 TCP 原理_第16张图片

  • 应答报文的区分
    • 后面ACK代表的就是应答报文ACK

【JaveEE】UDP 与 TCP 原理_第17张图片

3.2 应答报文ACK的作用

3.2.1 丢包

丢包在互联网里是一个很普遍的现象

  • 丢包的原因:信息在互联网中传输,要经过很多交换机和路由器,任何一个节点出现问题,都有可能会导致丢包
  • 网不好:ping、延时、丢包率高…
    • 每个设备都承担很多转发的任务
    • 每个设备转发能力有限
    • 某一时刻某个设备,流量达到峰值,就可能导致那一刻的数据被丢包!

【JaveEE】UDP 与 TCP 原理_第18张图片

  • 非常容易出现概率性丢包!
    • 一般只会丢一些
  • 打游戏(对抗性)出现丢包,体验感会很差

丢包率如果达到10%,你基本上已经玩不了游戏了

3.2.1 处理丢包现象 — 超时重传

如果发送方在一段时间后,迟迟没收到ACK,则发送方会判定为刚才的数据丢包了!

  • 发送方就会重新再发一遍,即 超时重传
  • 大概率重新发过去是能成功的,如果又失败了,超时重传即可

例如丢包率为10% ==> 丢包三次 0.1%,概率很低很低了,如果还丢包,那网络出现问题的概率更大!

  • 丢包很多次 ==> 丢包率50% + ,这就太离谱了

所以,TCP处理这种多个包丢失的情况,仍然会超时重传

  • 但是,每丢包异常,超时等待时间就会变长
  • 但是的但是,如果连续丢包很多次,多半是你网络出现严重问题了,TCP将尝试重连
    • 如果重连都失效,TCP就会关闭连接,放弃此次网络通讯

其实没有收到ACK有两种情况:

  1. 数据丢了当然就没有ACK
  2. 接收方收到数据了,但是发出ACK丢了
    • 发送方是区分不了这种情况的,一定会超时重传
    • 这样的话,接收方将收到重复的一个数据
    • 这很严重,要是是涉及钱的操作,重复扣款两次,那就糟糕了!

还记得TCP的序号机制吗

  • TCP会根据发过来的数据的序号,自动去重!
  • 保证应用程序读到的数据仍然只有一份

TCP太伟大了,T.T

  • 确认应答保证可靠性
  • 超时重传弥补丢包现象

这两个机制就是TCP的基石~

3.3 连接管理

问:TCP是如何实现可靠性的?

  • 正确答案:确认应答 + 超时重传
  • 误解:三次握手,四次挥手

但是,可靠性跟三次握手和四次挥手是有一点点关系的

  • 但是的但是,可靠性要由确认应答和超时重传严密细节地实现
    • 而三次握手和四次挥手只是保证“连接了”的这个前提罢了

3.3.1 TCP建立连接 — 三次握手

一次握手 (handshake):一次交互

三次握手表示客户端和服务器之间,通过三次交互,建立了连接关系

  • 连接:双方各自记录对方的信息

【JaveEE】UDP 与 TCP 原理_第19张图片

  1. 客户端发出请求连接的申请 【syn】
  2. 服务器返回 【ack】 确认连接
    • 并发送 【syn】,申请与客户端连接
  3. 客户端也返回 【ack】 确认连接

小例子,小马和老马语音聊天:

  1. 小马问:你那边听得到我的声音吗?
  2. 老马说:听得到
    • 你呢?
  3. 小马说:我也听得到

如果老马不问,老马就不知道小马听不听得到它的声音

就是这个道理

  • 客户端申请并要求服务器 “接受我的端请求和返回给我的响应”

  • 服务器回复ack就相当于接受连接,承诺接受客户端请求和返回响应

    • 服务器也要申请并要求客户端 “发送给我请求和接受我的响应”
  • 客户端回复ack就相当于接受连接,承诺发送给服务器请求和接受服务器的响应

这合乎常理,发送申请不代表我已经连接到他了,只是试探,而对方接受申请则是肯定接受我的连接(go!我答应干活了)了,对方也要发送个申请来连接我,我再接受他的连接,才算是真正的“连接”(试探成功,我也要努力干活)

而服务器的ack和syn合成一个数据报一起发送,这是合理省事的,因为在双方未正式连接完之前,服务器也不需要做啥,一起发送是才是个明智的选择

  • 见以下数据报报文结构分析

3.2.2 报文中特殊的六个比特位

【JaveEE】UDP 与 TCP 原理_第20张图片

  • 普通的数据报这六位都是0,但是如果出现1,就代表特殊的含义~

第二位为1:ack报文 ==> 应答报文

第五位为1:syn报文 ==> 同步报文

第二位和第五位都为1:ack + syn报文

  • 这就是三次握手的第二次握手发送的报文

第六位为1:fin报文 ==> 结束报文

其他几个不讲~

  • 见四次挥手

为什么三次握手:

  • 投石问路,验证客户端和服务器的发送能力与接受能力是否正常
    • 但是没有测“功底”
  • 投石问路,建立可靠传输的基础,但是不是远远达不到“严密保证”的作用的

3.3.3 TCP断开连接 — 四次挥手

  • 跟三次握手很像,但是有点差异
    • 主要是因为断开连接,要保证服务器那个线程的活要干完~
      【JaveEE】UDP 与 TCP 原理_第21张图片

一样的:

  • 客户端:“你可以断开连接吗”
  • 服务器:“ok”
    • 干完活后:“你也断开连接吧”
  • 客户端:“ok”

一般来说,服务器那个线程是不会立马结束的,所以ack和fin分两次发

  • 但是也有个别情况,ack和fin,应答报文和结束报文是同一个数据报发送

【JaveEE】UDP 与 TCP 原理_第22张图片

总流程:

【JaveEE】UDP 与 TCP 原理_第23张图片

3.4 TCP是如何挽救效率的

TCP为了可靠性,牺牲了效率

  • 因为要等待应答,超时重传…

单看效率,是完全比不上UDP的

  • 但是TCP也在挽救它的效率~

3.4.1 批量发送 — 滑动窗口

  • 减少等待应答的次数

批量发送:一次发多条数据,同时等待一个ack

【JaveEE】UDP 与 TCP 原理_第24张图片

批量发送不是无限制发送数据,而是发送到一定程度后,等待ack

  • 发太多对方也可能受不了

而这个限制就是窗口的大小~

【JaveEE】UDP 与 TCP 原理_第25张图片

  • 这就相当于用一单位的时间,去等待四单位的ack
    • 微观上看,每次窗口内部,都是同时在等的
    • 宏观上看,近似没等一单位时间,窗口快速滑动,四个数据几乎同时收到ack

动图演示:

  • 按序号处理后每返回一个ack,窗口滑动
  • 发送方立即发出新窗口内的新来的那条数据,立马发送

滑动窗口的丢包现象处理模式:

  • 可靠性重要程度肯定要大于效率
    • 不能为了效率而降低可靠性
  1. ack丢失

这种情况,完全没有影响,因为应答报文有个应答序号,其含义是其之前的数据都正常读到

  • 那么一个ack就可以代表其之前的所有ack
  • 那么最后的ack没了咋办,超时重传 ==> 缓冲区去重,接收方重新发ack

动图演示:

  • 中途没滑的,后面一次性滑过去
  1. 数据包丢失
    • 1001-2000的数据丢失,那么主机B就一直发的是应答序号为1001的应答报文,去索要1001开头的数据
      • 收到了2001-7000的数据,但是应答的还是1001
      • 2001-7000在缓冲区里呆着呢
    • 到达一定次数后/超时重传,机器A重新发送1001-2000的数据
      • 处理完1001-2000的数据以及早就收到了的2001-7000的数据后,发出7001的ack

【JaveEE】UDP 与 TCP 原理_第26张图片

动图演示:

  • 这个重传过程没有任何冗余操作,整体效率很高,也被称为“快速重传”

而,如果批量发送太多,挤爆了缓冲区怎么办?

  • “流量控制”

3.4.2 流量控制

流量控制就是在批量发送的时候,限制滑动窗口的大小,不能导致接收方的缓冲区炸了

窗口越大 ==> 速度越快 ==> 更容易让缓冲区炸

  • 缓冲区炸了,多余的数据全部丢包!在这里插入图片描述

  • 所以流量控制能够保证一定的可靠性

机制:

  1. 发送一小批的数据去试探
  2. 返回的ack,传递了一个信息,“窗口大小”的字段:
    • 这里的值就是,建议发送方发送的窗口大小
    • 计算这个窗口大小的方法很简单粗暴,缓冲区剩多少就是多少

【JaveEE】UDP 与 TCP 原理_第27张图片

【JaveEE】UDP 与 TCP 原理_第28张图片

此图的动图表示为:

  • 如果窗口大小为0,就需要再一个ack去告诉他位置腾出来了
    • socket读数据消费缓冲区的内容
    • 等待等待等待~
    • 有点类似阻塞队列
  • 但是时时等不到,所以超时重传(窗口探测) — 4001-4001

但是实际上,实际的限制的窗口大小不等于缓冲区剩余的空间大小

  • 这也是为什么刚才说是“建议”

真正的滑动窗口大小 = f ( 流量控制, 拥塞控制 )

3.4.3 拥塞控制

流量控制:

  • 衡量了接收方的处理能力

拥塞控制:

  • 衡量路径上的处理能力

我们知道,数据在互联网的传输路径上,会经过很多个路由器和交换机

  • 那么,只要中间一个设备承受不了这个流量,那么就会导致不可靠传输,即丢包

【JaveEE】UDP 与 TCP 原理_第29张图片

就像耳熟能详的“木桶效应”:

【JaveEE】UDP 与 TCP 原理_第30张图片

真正的滑动窗口大小,应该是通过综合考虑到接收方缓冲区剩余空间,中间路径节点的个数和承受能力。

  • 并且,每次传输数据也不一定是按照刚才的路径去传输的,所以无法准确得出数值

最好的方法就是:实验practice,实践出真理,通过实验找到一个合适的发送速率。

  1. 一开始的时候按照小速率发送,如果不丢包(丢包一律视为承受能力不足),就可以提高一下速率(扩大窗口大小)
  2. 提高提高提高
  3. 直到丢包了,把窗口调小
    • 有确认应答和超时重传处理丢了的包
  4. 重复上述过程
  • 处于动态平衡的过程

【JaveEE】UDP 与 TCP 原理_第31张图片

  • 指数增长快速到达ssthresh(阈值)后,加法增长稳步上升(减少丢包率)
  • 丢包后,重新去做实验,并且ssthresh阈值更新(变大 / 变小 /不变)

【JaveEE】UDP 与 TCP 原理_第32张图片

拥塞窗口:

  • 拥塞控制实验出来的窗口

实际窗口就是:min{拥塞窗口, 流量窗口}

3.4.4 延时应答

TCP的可靠性核心为:确认应答

  • ACK要发,但是不是立即发,而是等一小会儿再发~

但是通过刚才对TCP的了解,决定传输效率的关键因素为“窗口大小”

实际窗口就是:min{拥塞窗口, 流量窗口}

  • 要是你ack立马发过去,那就有数据立马发过来,那么就相当于传输过来的数据又多了,可能会导致窗口变小~

相当于说:

别人发任务给我,我完成了一部分,不立马说我完成了一部分(那么它就会给我补一点任务),而是完成百分之70后才跟他说,这样压力就少很多~

这个ack延时发送后,窗口大小大概率是变大了的,因为这个过程里,接受方一直消耗接受缓冲区里的内容~

  • 数量控制:每隔N个包就应答一次
  • 时间控制:超过最大延时时间就应答一次

看情况处理,也有一些包是不会延时的

3.4.5 捎带应答

  • 基于延时应答

客户端服务器之间的通信,一般是 “一问一答” 的模式

  • 上传大文件的时候,可能就会分为多个包请求,即 “多问一答”
  • 下载大文件的时候,可能就会分为多个包响应,即 “一问多答”
  • “多问多答”,游戏串流~(streamlink / moonlight)
    • pc成为服务器,手机成为客户端
    • 手机一系列请求给pc,pc返回一系列响应
    • 复杂,并不是一条对着一条的~

对于一问一答的形式:

【JaveEE】UDP 与 TCP 原理_第33张图片

ack原本是立马返回的,但是结合延时应答,如果延时应答过程中,响应计算好了,或者时机很接近,那么ack就和响应一起打包发送,这就是捎带应答

【JaveEE】UDP 与 TCP 原理_第34张图片

  • 合成一个比分两次发,效率更高

所以说,四次挥手有时可能是三次挥完呢?

  • 原因就是延时应答,捎带应答

3.5 面向字节流

  • 多个TCP的数据报,在接收方接收缓冲区里,被拆分为应用层数据报,紧紧挨着,那么就会出现“粘包问题

【JaveEE】UDP 与 TCP 原理_第35张图片

  • 我们分不清这三个请求哪跟哪的~

定义报与报之间的分割符,这就是一个有效方案

  • 这里,我们就可以定义,说完话要加句号或者问号~

还有一个常见方案:约定数据的前4个字节(或者更多),表示整个数据报的长度

  • 这些都是应用层数据报协议的注意事项~
  • 协议里会提到报的数据格式,和报的边界

3.6 异常情况

3.6.1 进程关闭 / 进程崩溃

  • 进程没了,socket文件关闭(发出fin),连接还在,四次挥手正常进行
  • 主机还醒着,就可以回应ack

3.6.2 主机关机(正常流程关机)

  • 杀死所有用户进程 ==> 进程没了
    • socket文件关闭(发出fin),连接还在,四次挥手正常进行
    • 主机还醒着,就可以回应ack
  • 如果四次挥手没挥完,对方就会重传fin,多次无果重置连接,再不行就释放连接了

3.6.3 主机掉电(因为电的原因强制快速关机)

  • 肯定来不及做任何挥手操作
  1. 对方是发送方,对方就会收不到ack ==> 超时重传 ==> 重置连接 ==> 释放连接

  2. 对方是接收方,对方是没法立即知道,我这边是没来来得及发新的数据,还是我“死了”

    • 我可能在控制台输入中呢~

    • TCP内置了心跳包的“保活机制”

    1. 周期性
    2. 如果心跳没了,你就是“死了”

就是说,对方是接收方,对方就会定期给我发一个心跳包(ping),我就返回一个(pong)

  • 如果每个ping,都有即使的pong,那么就说明我“没死”,机器良好
    • 我如果在控制台输入中或者等等情况下,机器正常,就会收到ping,返回pong,这个不需要代码控制
  • 如果ping没收到pong,超时重传 ==> 重载连接 ==> 释放连接

例子:

  • 我设置了一个炸弹,我如果死了,炸弹就会爆炸。炸弹每一段时间就会发送给我手腕上的手表一个ping,如果我有脉搏,手表就会返回一个pong,如果我死了,就不会有pong,炸弹就会爆炸

3.6.4 网线断开

  • 来不及任何的挥手操作,跟第三点是一样的

4. TCP十大核心机制小节

  1. 确认应答
  2. 超时重传
  3. 连接管理
  4. 滑动窗口
  5. 流量控制
  6. 拥塞控制
  7. 延时应答
  8. 捎带应答
  9. 面向字节流
    • 粘包问题
  10. 异常处理
    • 心跳包

更多TCP的细节,可以查看这个标准文档:

RFC 9293: Transmission Control Protocol (TCP) (ietf.org)

5. TCP 与 UDP的差别

TCP 可靠传输,效率没那么高,UDP不可靠传输,效率高~

两者的不同在于应用场景的不同

  • 绝大多数情况下,都可以使用TCP
  • 但是对于一些要求高效率,对可靠性要求不高的情况下,UDP会更好
    • 例如,机房内部的内网之间的数据传输 / 分布式系统~

场景:既需要可靠性,又需要比较高的效率

  • 这两种协议都不合适,而传输层协议当然不止有这两种,如果你感兴趣的话,可以去了解哪些协议更适合处理这样的场景

文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭

网络原理尚未结束,IP协议紧随其后,敬请期待~


你可能感兴趣的:(JavaEE,udp,tcp/ip,网络)