这篇博客真的很详细很详细很详细,不打算试试看吗 > 。o
文章目录
- 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协议,应用层的代表协议,到时候重点讲解
而大多数时候,都是程序员自定义组织的协议~
- 而字段与字段之间如何组织呢?
约定:
- 传输哪些数据
- 按甲方爸爸的需求以及实际需要的
- 组织格式
- 随意约定
常见组织格式:
- 分隔符
- 以空格、回车…分割
- 以分号…表示结束
实际开发会使用一些现成的格式:
- xml(基本认识)
- html是特殊的xml格式
- html标签名和含义都是固定的,是我们需要遵守的
标签的形式:< XXX > … </ XXX>
- json(基本认识)
-
以{ }作为标识
-
内部有多个键值对,每个键值对之间用逗号分割
-
key与值之间使用冒号分割
- key必须是字符串,
- 值可以是数组,字符串,数字甚至是另一个json
2. 传输层UDP协议
- 柿子要挑软的捏~
2.1 数据报报文格式
- 这是教科书上的画法~
其实并不准确,真实的报文应该是这样的:
2.1.1 源端口与目的端口
可能有人会想,服务器不是远在天边的“服务我们的机器”吗?
- 然而并不绝对,一些服务器可以是我们本地主机上的一个程序
- 例如http或者mysql
http有个专属“座位“:80
- ssh:22
- ftp:21
- …
- “端口0”并不正式存在。 它被定义为无效的端口号。
并不是说这些端口号就代表了“远方服务器”的位置!
- 端口号:主机上程序的位置!
至于“专属座位”有什么用
- 服务器一般端口号不需要怎么改变,所以保持在一个端口号是个正常的抉择
- 不然每次你都要去查这个常用的服务器端口号是多少~
2.1.2 报文长度和校验和
报文长度:0~65535 => 64KB(64KB - 1B,近似64KB)
- 代表UDP正文最大就是64KB大而已
- 不是说只能传一个“65535”大小的整数~
- 而是65535字节的数据量!
时代不同,现在64KB很小了,一个表情包都能几MB了~
- 但是如果要扩大这个限制,那么我们就要升级UDP系统
- 只有都升级,才能通讯,这不现实~
要传输大数据
- 把数据拆分为多个数据,用多个UDP数据报传输~
- 不用UDP,用TCP去传输 > v o
- TCP就没有限制
所以,UDP的数据报不宜过长,否则会出错
校验和:
- 我们传递的数据本质是01…,而我们用高低电平表示01
- 难免会有一些干扰,例如强磁场(太阳黑子)
- 这样就会导致一些高电平转变为低电平,低电平转变为高电平
- 那么校验和的存在,就相当于定下了一个标准
- 满足这个标准,数据不一定对;但是不满足这个标准,这个数据肯定错
- 而这个校验和,要是中间数据发生01转变,大概率是对不上的
- 可能校验和跟数据发生突变后,还对上了。很极端的情况
- 例如:
- 小马去太空,原本五根手指,回来变成四根,”校验和为5根手指“,这样就对不上了~
- 回来还是五根,但是细节不对,但是的但是蒙混过关了~
- 校验和突变为“4根手指”,反而对上了(极端)
校验和是通过复杂计算后的一个数值~
- 发送方:
- 接收方:
- 有很多种方式,例如CRC算法,奇偶校验…
- 对不上就数据就一定不正确!
通过上述结构,也可以看出UDP并不在意数据传输是否成功~
- 但是却有校验和校验数据正确性 > v <
3. 传输层TCP协议
核心机制,在于接收方收到了或者没有收到,都会有个应答,对传输失败有所动作
- 教人知识也是如此,我如果教一个同学一个知识,他没有如何应答,我就没法判断他掌握了没有~
- 会了:我继续讲~
- 不会:我重新讲~
数据报报文结构:
3.1 TCP是如何保证可靠传输 — 确认应答
可靠性不等于安全性
- 安全性是指容不容易被黑客篡改 / 窃取
- 可靠性是指数据传输是否能正确到达对方
应答报文规则:
-
单条信息发送:
- 发送方没收到应答是不会继续发消息的
- 发送方没收到应答是不会继续发消息的
-
多条信息发送:
- socket里有个接受缓冲区,里面就会把序列排好,再被接收方读取~
- 应答报文的区分
- 后面ACK代表的就是应答报文ACK
3.2 应答报文ACK的作用
3.2.1 丢包
丢包在互联网里是一个很普遍的现象
- 丢包的原因:信息在互联网中传输,要经过很多交换机和路由器,任何一个节点出现问题,都有可能会导致丢包
- 网不好:ping、延时、丢包率高…
- 每个设备都承担很多转发的任务
- 每个设备转发能力有限
- 某一时刻某个设备,流量达到峰值,就可能导致那一刻的数据被丢包!
- 非常容易出现概率性丢包!
- 一般只会丢一些
- 打游戏(对抗性)出现丢包,体验感会很差
丢包率如果达到10%,你基本上已经玩不了游戏了
3.2.1 处理丢包现象 — 超时重传
如果发送方在一段时间后,迟迟没收到ACK,则发送方会判定为刚才的数据丢包了!
- 发送方就会重新再发一遍,即 超时重传
- 大概率重新发过去是能成功的,如果又失败了,超时重传即可
例如丢包率为10% ==> 丢包三次 0.1%,概率很低很低了,如果还丢包,那网络出现问题的概率更大!
- 丢包很多次 ==> 丢包率50% + ,这就太离谱了
所以,TCP处理这种多个包丢失的情况,仍然会超时重传
- 但是,每丢包异常,超时等待时间就会变长
- 但是的但是,如果连续丢包很多次,多半是你网络出现严重问题了,TCP将尝试重连
- 如果重连都失效,TCP就会关闭连接,放弃此次网络通讯
其实没有收到ACK有两种情况:
- 数据丢了当然就没有ACK
- 接收方收到数据了,但是发出ACK丢了
- 发送方是区分不了这种情况的,一定会超时重传
- 这样的话,接收方将收到重复的一个数据
- 这很严重,要是是涉及钱的操作,重复扣款两次,那就糟糕了!
还记得TCP的序号机制吗
- TCP会根据发过来的数据的序号,自动去重!
- 保证应用程序读到的数据仍然只有一份
TCP太伟大了,T.T
- 确认应答,保证可靠性
- 超时重传,弥补丢包现象
这两个机制就是TCP的基石~
3.3 连接管理
问:TCP是如何实现可靠性的?
- 正确答案:确认应答 + 超时重传
- 误解:三次握手,四次挥手
但是,可靠性跟三次握手和四次挥手是有一点点关系的
- 但是的但是,可靠性要由确认应答和超时重传严密细节地实现
- 而三次握手和四次挥手只是保证“连接了”的这个前提罢了
3.3.1 TCP建立连接 — 三次握手
一次握手 (handshake):一次交互
三次握手表示客户端和服务器之间,通过三次交互,建立了连接关系
- 连接:双方各自记录对方的信息
- 客户端发出请求连接的申请 【syn】
- 服务器返回 【ack】 确认连接
- 并发送 【syn】,申请与客户端连接
- 客户端也返回 【ack】 确认连接
小例子,小马和老马语音聊天:
- 小马问:你那边听得到我的声音吗?
- 老马说:听得到
- 你呢?
- 小马说:我也听得到
如果老马不问,老马就不知道小马听不听得到它的声音
就是这个道理
-
客户端申请并要求服务器 “接受我的端请求和返回给我的响应”
-
服务器回复ack就相当于接受连接,承诺接受客户端请求和返回响应
- 服务器也要申请并要求客户端 “发送给我请求和接受我的响应”
-
客户端回复ack就相当于接受连接,承诺发送给服务器请求和接受服务器的响应
这合乎常理,发送申请不代表我已经连接到他了,只是试探,而对方接受申请则是肯定接受我的连接(go!我答应干活了)了,对方也要发送个申请来连接我,我再接受他的连接,才算是真正的“连接”(试探成功,我也要努力干活)
而服务器的ack和syn合成一个数据报一起发送,这是合理省事的,因为在双方未正式连接完之前,服务器也不需要做啥,一起发送是才是个明智的选择
- 见以下数据报报文结构分析
3.2.2 报文中特殊的六个比特位
- 普通的数据报这六位都是0,但是如果出现1,就代表特殊的含义~
第二位为1:ack报文 ==> 应答报文
第五位为1:syn报文 ==> 同步报文
第二位和第五位都为1:ack + syn报文
- 这就是三次握手的第二次握手发送的报文
第六位为1:fin报文 ==> 结束报文
其他几个不讲~
- 见四次挥手
为什么三次握手:
- 投石问路,验证客户端和服务器的发送能力与接受能力是否正常
- 但是没有测“功底”
- 投石问路,建立可靠传输的基础,但是不是远远达不到“严密保证”的作用的
3.3.3 TCP断开连接 — 四次挥手
- 跟三次握手很像,但是有点差异
- 主要是因为断开连接,要保证服务器那个线程的活要干完~
- 主要是因为断开连接,要保证服务器那个线程的活要干完~
一样的:
- 客户端:“你可以断开连接吗”
- 服务器:“ok”
- 干完活后:“你也断开连接吧”
- 客户端:“ok”
一般来说,服务器那个线程是不会立马结束的,所以ack和fin分两次发
- 但是也有个别情况,ack和fin,应答报文和结束报文是同一个数据报发送
总流程:
3.4 TCP是如何挽救效率的
TCP为了可靠性,牺牲了效率
- 因为要等待应答,超时重传…
单看效率,是完全比不上UDP的
- 但是TCP也在挽救它的效率~
3.4.1 批量发送 — 滑动窗口
- 减少等待应答的次数
批量发送:一次发多条数据,同时等待一个ack
批量发送不是无限制发送数据,而是发送到一定程度后,等待ack
- 发太多对方也可能受不了
而这个限制就是窗口的大小~
- 这就相当于用一单位的时间,去等待四单位的ack
- 微观上看,每次窗口内部,都是同时在等的
- 宏观上看,近似没等一单位时间,窗口快速滑动,四个数据几乎同时收到ack
动图演示:
- 按序号处理后每返回一个ack,窗口滑动
- 发送方立即发出新窗口内的新来的那条数据,立马发送
滑动窗口的丢包现象处理模式:
- 可靠性重要程度肯定要大于效率
- 不能为了效率而降低可靠性
- ack丢失
这种情况,完全没有影响,因为应答报文有个应答序号,其含义是其之前的数据都正常读到
- 那么一个ack就可以代表其之前的所有ack
- 那么最后的ack没了咋办,超时重传 ==> 缓冲区去重,接收方重新发ack
动图演示:
- 中途没滑的,后面一次性滑过去
- 数据包丢失
- 1001-2000的数据丢失,那么主机B就一直发的是应答序号为1001的应答报文,去索要1001开头的数据
- 收到了2001-7000的数据,但是应答的还是1001
- 2001-7000在缓冲区里呆着呢
- 到达一定次数后/超时重传,机器A重新发送1001-2000的数据
- 处理完1001-2000的数据以及早就收到了的2001-7000的数据后,发出7001的ack
- 1001-2000的数据丢失,那么主机B就一直发的是应答序号为1001的应答报文,去索要1001开头的数据
动图演示:
- 这个重传过程没有任何冗余操作,整体效率很高,也被称为“快速重传”
而,如果批量发送太多,挤爆了缓冲区怎么办?
- “流量控制”
3.4.2 流量控制
流量控制就是在批量发送的时候,限制滑动窗口的大小,不能导致接收方的缓冲区炸了
窗口越大 ==> 速度越快 ==> 更容易让缓冲区炸
-
缓冲区炸了,多余的数据全部丢包!在这里插入图片描述
-
所以流量控制能够保证一定的可靠性
机制:
- 发送一小批的数据去试探
- 返回的ack,传递了一个信息,“窗口大小”的字段:
- 这里的值就是,建议发送方发送的窗口大小
- 计算这个窗口大小的方法很简单粗暴,缓冲区剩多少就是多少
此图的动图表示为:
- 如果窗口大小为0,就需要再一个ack去告诉他位置腾出来了
- socket读数据消费缓冲区的内容
- 等待等待等待~
- 有点类似阻塞队列
- 但是时时等不到,所以超时重传(窗口探测) — 4001-4001
但是实际上,实际的限制的窗口大小不等于缓冲区剩余的空间大小
- 这也是为什么刚才说是“建议”
真正的滑动窗口大小 = f ( 流量控制, 拥塞控制 )
3.4.3 拥塞控制
流量控制:
- 衡量了接收方的处理能力
拥塞控制:
- 衡量路径上的处理能力
我们知道,数据在互联网的传输路径上,会经过很多个路由器和交换机
- 那么,只要中间一个设备承受不了这个流量,那么就会导致不可靠传输,即丢包
就像耳熟能详的“木桶效应”:
真正的滑动窗口大小,应该是通过综合考虑到接收方缓冲区剩余空间,中间路径节点的个数和承受能力。
- 并且,每次传输数据也不一定是按照刚才的路径去传输的,所以无法准确得出数值
最好的方法就是:实验practice,实践出真理,通过实验找到一个合适的发送速率。
- 一开始的时候按照小速率发送,如果不丢包(丢包一律视为承受能力不足),就可以提高一下速率(扩大窗口大小)
- 提高提高提高
- 直到丢包了,把窗口调小
- 有确认应答和超时重传处理丢了的包
- 重复上述过程
- 处于动态平衡的过程
- 指数增长快速到达ssthresh(阈值)后,加法增长稳步上升(减少丢包率)
- 丢包后,重新去做实验,并且ssthresh阈值更新(变大 / 变小 /不变)
拥塞窗口:
- 拥塞控制实验出来的窗口
实际窗口就是:min{拥塞窗口, 流量窗口}
3.4.4 延时应答
TCP的可靠性核心为:确认应答
- ACK要发,但是不是立即发,而是等一小会儿再发~
但是通过刚才对TCP的了解,决定传输效率的关键因素为“窗口大小”
实际窗口就是:min{拥塞窗口, 流量窗口}
- 要是你ack立马发过去,那就有数据立马发过来,那么就相当于传输过来的数据又多了,可能会导致窗口变小~
相当于说:
别人发任务给我,我完成了一部分,不立马说我完成了一部分(那么它就会给我补一点任务),而是完成百分之70后才跟他说,这样压力就少很多~
这个ack延时发送后,窗口大小大概率是变大了的,因为这个过程里,接受方一直消耗接受缓冲区里的内容~
- 数量控制:每隔N个包就应答一次
- 时间控制:超过最大延时时间就应答一次
看情况处理,也有一些包是不会延时的
3.4.5 捎带应答
- 基于延时应答
客户端服务器之间的通信,一般是 “一问一答” 的模式
- 上传大文件的时候,可能就会分为多个包请求,即 “多问一答”
- 下载大文件的时候,可能就会分为多个包响应,即 “一问多答”
- “多问多答”,游戏串流~(streamlink / moonlight)
- pc成为服务器,手机成为客户端
- 手机一系列请求给pc,pc返回一系列响应
- 复杂,并不是一条对着一条的~
对于一问一答的形式:
ack原本是立马返回的,但是结合延时应答,如果延时应答过程中,响应计算好了,或者时机很接近,那么ack就和响应一起打包发送,这就是捎带应答
- 合成一个比分两次发,效率更高
所以说,四次挥手有时可能是三次挥完呢?
- 原因就是延时应答,捎带应答
3.5 面向字节流
- 多个TCP的数据报,在接收方接收缓冲区里,被拆分为应用层数据报,紧紧挨着,那么就会出现“粘包问题”
- 我们分不清这三个请求哪跟哪的~
定义报与报之间的分割符,这就是一个有效方案
- 这里,我们就可以定义,说完话要加句号或者问号~
还有一个常见方案:约定数据的前4个字节(或者更多),表示整个数据报的长度
- 这些都是应用层数据报协议的注意事项~
- 协议里会提到报的数据格式,和报的边界
3.6 异常情况
3.6.1 进程关闭 / 进程崩溃
- 进程没了,socket文件关闭(发出fin),连接还在,四次挥手正常进行
- 主机还醒着,就可以回应ack
3.6.2 主机关机(正常流程关机)
- 杀死所有用户进程 ==> 进程没了
- socket文件关闭(发出fin),连接还在,四次挥手正常进行
- 主机还醒着,就可以回应ack
- 如果四次挥手没挥完,对方就会重传fin,多次无果重置连接,再不行就释放连接了
3.6.3 主机掉电(因为电的原因强制快速关机)
- 肯定来不及做任何挥手操作
-
对方是发送方,对方就会收不到ack ==> 超时重传 ==> 重置连接 ==> 释放连接
-
对方是接收方,对方是没法立即知道,我这边是没来来得及发新的数据,还是我“死了”
-
我可能在控制台输入中呢~
-
TCP内置了心跳包的“保活机制”
- 周期性
- 如果心跳没了,你就是“死了”
-
就是说,对方是接收方,对方就会定期给我发一个心跳包(ping),我就返回一个(pong)
- 如果每个ping,都有即使的pong,那么就说明我“没死”,机器良好
- 我如果在控制台输入中或者等等情况下,机器正常,就会收到ping,返回pong,这个不需要代码控制
- 如果ping没收到pong,超时重传 ==> 重载连接 ==> 释放连接
例子:
- 我设置了一个炸弹,我如果死了,炸弹就会爆炸。炸弹每一段时间就会发送给我手腕上的手表一个ping,如果我有脉搏,手表就会返回一个pong,如果我死了,就不会有pong,炸弹就会爆炸。
3.6.4 网线断开
- 来不及任何的挥手操作,跟第三点是一样的
4. TCP十大核心机制小节
- 确认应答
- 超时重传
- 连接管理
- 滑动窗口
- 流量控制
- 拥塞控制
- 延时应答
- 捎带应答
- 面向字节流
- 粘包问题
- 异常处理
- 心跳包
更多TCP的细节,可以查看这个标准文档:
RFC 9293: Transmission Control Protocol (TCP) (ietf.org)
5. TCP 与 UDP的差别
TCP 可靠传输,效率没那么高,UDP不可靠传输,效率高~
两者的不同在于应用场景的不同
- 绝大多数情况下,都可以使用TCP
- 但是对于一些要求高效率,对可靠性要求不高的情况下,UDP会更好
- 例如,机房内部的内网之间的数据传输 / 分布式系统~
场景:既需要可靠性,又需要比较高的效率
- 这两种协议都不合适,而传输层协议当然不止有这两种,如果你感兴趣的话,可以去了解哪些协议更适合处理这样的场景
文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆!网络原理尚未结束,IP协议紧随其后,敬请期待~