1.前言
哈喽大家好呀,好久没有给大家继续带来关于Java网络原理的学习了,前一段时间网络原理的学习是大部分关于应用层的,接下来就该进入传输层的详细讲解了,今天主要给大家分享的是传输层的两大核心协议——UDP与TCP,前面学习有提及过一点点,这篇博文就给它详细讲解完。
这里简单科普俩句,如果做业务开发的,UDP/TCP更少,HTTP更多;如果做的是基础架构开发,UDP/TCP更多,HTTP更少。
UDP:无连接,不可靠传输,面向数据包,全双工~~
先总体概览下:
端口号:
各两个字节,共32bit位。一个端口号的取值范围,0->65535。
实际上,一般把 1024 以下的端口保留,咱们写代码都是用1024->65535 这个范围的,如果设置不在这个范围内,非法端口号。
长度:
长度由报头+载荷总长度组成。
校验和:
验证数据是否发生修改的手段。
(光信号,电信号,电磁波,收到外界干扰可能会使高低电平/高低频光信号发生改变)
校验流程:
另外,如果两个校验和相同,原始数据一定也相同可能存在变数,这个变数即有极小概率会出现这种情况:前一个字节bit翻转刚好小了1,后一个字节bit翻转刚好大了1,最终加到一起,校验和是一样的。虽说原理上有这种情况存在,但比特翻转本身极小概率,恰好两个翻转抵消了影响,小之又小。
载荷:
就是数据本身了~
核心特点如下:
- 无需握手过程:"即发即走"模式
- 示例:DNS查询直接发送请求包,无需预先建立连接不可靠传输
- 不保证数据到达
- 不保证顺序正确
- 无重传机制
- 网络拥堵时仍按原速率发送
- 优势:避免TCP的"减速等待"问题
- 固定8字节开销(TCP至少20字节)
- 减少网络传输负担
UDP的不可靠性体现在三个层面:
✅ 设计哲学:
UDP的"不可靠"本质是用可靠性换取性能,适合能容忍丢包的场景(如视频通话丢几帧不影响整体)
与TCP的字节流不同,UDP保持应用层消息边界:
# 发送端sendto("Hello".encode()) # 发送5字节数据报sendto("World".encode()) # 发送5字节数据报 # 接收端data1 = recvfrom() # 收到完整"Hello"data2 = recvfrom() # 收到完整"World"AI写代码
核心特征:
协议 | 端口 | 应用场景 | 可靠性实现 |
---|---|---|---|
DNS | 53 | 域名解析 | 应用层重试 |
DHCP | 67/68 | IP地址分配 | 广播+超时重试 |
NTP | 123 | 时间同步 | 冗余采样 |
TFTP | 69 | 简单文件传输 | 块确认+重传 |
RTP | 动态 | 实时音视频 | 序号+时间戳 |
QUIC | 443 | HTTP/3底层 | 自定义可靠传输 |
TCP:有连接,面向字节流,可靠传输,全双工
传输层核心内容:16位源端口号+16位目的端口号 首部长度:选项的存在,导致tcp报头长度是可变的 保留:UDP 的问题,长度不够,又不能扩展~~TCP 的设计者就考虑到这样的问题。TCP 报头中就预留了一些“保留位”(现在先不用,但是占个位子)。 标志位:TCP最核心的六个标志位(里面有俩个较为少见的,所以说六个~) 16位校验和:用来校验数据是否出现错误的。
序号与确认号:
一个TCP 的载荷是多个字节构成的~~ 每个字节都分配一个编号,并且是连续递增的。序号字段填写载荷部分的第一个字节的序号,序号连续递增。
引入序号之后,接收方就可以根据序号对数据进行排序~~这里需要引入后发先至的概念啦~TCP 需要处理后发先至的情况,确保应用程序通过 socket api 读到的数据顺序是正确的~。
TCP 在接收方这里会安排"接收缓冲区"(内存,操作系统内核里)通过网卡读到的数据,先放到接收缓冲区中,后续代码里调用read) ,也是从接受缓冲区来读的。
根据序号来排序,序号小的在前面,大的在后面确保前面的数据已经到了,然后 read 才能接触。如果是后面的数据先到,read 继续阻塞,不会读取到数据。
基于 TCP 写代码的时候,完全不必担心数据顺序的问题~(代码写起来就方便了)
如果是基于 UDP,实现拆包组包,,就需要考虑顺序,自己实现排序逻辑~~
可靠性:网络通信,是非常复杂的此处的可靠性,不是说A给B发消息,B100% 能收到~而是 A 给 B发了消息之后,尽可能的让B收到~并且还要让A 能够知道 B 是否收到了~~
2.2.2.1确认应答
核心作用:保障数据传输的可靠性与有序性,解决网络传输中的丢包、乱序问题。
工作流程: sequenceDiagram Sender->>Receiver:发送数据包(Seq=100,Len=100,数据:100-199) Note right of Receiver:收到完整数据 Receiver->>Sender:回复ACK(Ack=200) Sender->>Sender:滑动窗口右移,释放缓冲区AI写代码
- 每个字节的唯一编号(初始值随机,防攻击)
- 例如:发送100字节数据,Seq=100 → 覆盖100~199号字节
- **期望收到的下一字节编号**
- Ack=200 表示199号及之前所有字节已确认收到
- Ack=N 意味着所有小于N的字节**均已正确接收**
- 例如:收到Ack=300后,无需再确认Seq=250的包
TCP头部中实现ACK的字段:
+-+-+-+-+-+-+-+-+| 控制标志(6位) || ...ACK=1... | → ACK标志位必须置1+-+-+-+-+-+-+-+-+| 确认号(32位)| → 携带Ack值(期望的下字节编号)+-+-+-+-+-+-+-+-+AI写代码
对比UDP的可靠性实现:
特性TCP确认应答应用层自实现(如QUIC)可靠性保证内核层自动完成用户空间逻辑控制性能开销每个数据包需ACK可批量确认(如每10包确认1次)实时性依赖RTT(通常10-100ms)可定制确认策略
🔍 设计哲学:
通过空间换时间(增加ACK头部开销),换取100%数据可达性,适用于对可靠性要求极高的场景(如金融交易、文件传输)
2.2.2.2超时重传
针对丢包的情况做出处理 ~~
核心概念讲解: 丢包产生的原因:
如何判断丢包:
达到等待时间的上限,还没有收到 ack,A 就认为传输中发生丢包了:
假设当前 A ->B 发送数据,丢包的超时时间阈值为T,当 A 给 B传输发生超时之后,就会延长这个时间阈值,会继续延长这个时间,这个时间不是无休止的。超时次数达到一定程度/等待时间达到一定程度,认为网络出现严重故障,放弃这一次传输~
时间阈值怎么来的:
俩种丢包情况:
对于A而言,无法分辨这俩种情况,则都是对数据进行重传,B就不一样了。在第二种情况下,B会收到两份相同的数据,这个时候TCP会在内部进行去重操作,根据序号在缓冲区寻找。
核心流程解析:
- 发送数据包(如Seq=100, Len=100)
- 同时启动**重传计时器**(初始RTO通常为1秒)
确认应答+超时重传 vs 三次握手
未来面试的时候很容易有一个误解的概念,这里加以区分:保证TCP可靠传输的是确认应答+超时重传机制而不是“三次握手”。
本质区别:阶段与目的不同
机制作用阶段核心目的对可靠传输的贡献确认应答+超时重传数据传输阶段保障数据包可靠传输直接保证三次握手连接建立阶段初始化通信参数间接基础(非直接保证)
技术本质分析
权威佐证(RFC 793)
TCP标准定义(4.2节):
“Reliability is achieved by assigning a sequence number to each octet transmitted, and requiring a positive acknowledgment (ACK) from the receiving party. If the ACK is not received within a timeout interval, the data is retransmitted.”
关键翻译:
“可靠性通过为每个传输的字节分配序列号,并要求接收方返回确认(ACK)来实现。如果在超时间隔内未收到ACK,数据将被重传。”
2.2.2.3连接管理
连接管理,包括建立连接与断开连接,建立连接采用的是“三次握手”的方式实现,而断开连接是富贵采用的“四次挥手”。 |
---|
图片引自网络,侵删
三次握手解析:
1. 核心原理
- _相当于敲门:"有人在吗?我想建立连接"_
- _相当于回应:"我在!请确认你收到"_
- _相当于确认:"收到!开始通信吧"_
2. 解决的问题
- 第一次握手:服务端确认**客户端发送能力**正常
- 第二次握手:客户端确认**服务端收发能力**正常
- 第三次握手:服务端确认**客户端接收能力**正常
3. 为什么不是两次?
4. 为什么不是四次?
四次挥手解析:
1. 核心原理
- _相当于说:"我说完了"_
- _相当于回应:"知道了"_
- _相当于说:"我也说完了"_
- _相当于回应:"好的,再见"_
2. 为什么需要四次?
3. TIME_WAIT状态的意义
1. 确保最后一个ACK到达(可重传)
2. 让网络中旧报文失效(防止新连接混淆)
4. 为什么不能是三次?
三、核心问题解答
Q1:为什么建立连接三次,断开却要四次?
建立连接断开连接特点双方无数据传输双向通道独立关闭动作纯控制报文被动方需处理残留数据合并服务端SYN+ACK可合并被动方ACK+FIN可条件合并
Q2:握手/挥手失败如何处理?
握手失败:
挥手失败:
2.2.2.4滑动窗口
想必大家听到这个词也是耳熟能详了,算法当中滑动窗口可是鼎鼎大名,但事实上算法上的滑动窗口就是来源于TCP中的~,因为TCP在保证可靠性的时候,付出了效率的代价,所以滑动窗口的设计就是为了提高点效率~
一、滑动窗口是什么?
通俗比喻:
把网络想象成一条流水线,滑动窗口就是允许连续作业的区域
技术定义:
二、核心组成结构
- `SND.UNA`:滑动窗口左边界(Send Unacknowledged)
- `SND.NXT`:下一个要发送的数据位置(Send Next)
- **窗口大小** = `WIN.END - SND.UNA`(动态变化)
1. 初始状态:窗口覆盖字节1-200
2. 发送字节1-100 → 进入**已发未确认**区域
3. 收到ACK=101 → 窗口**向右滑动**,101-200变为可发送区
4. 新窗口覆盖101-300 → 继续发送新数据
三、滑动窗口四大核心作用
- 接收方通过窗口字段通告可用缓冲区
- 发送方动态调整发送速率
- 窗口内数据必须被确认
- 未确认数据会重传
- 拥塞窗口(cwnd)限制最大发送量
- 与通告窗口取最小值作为实际窗口
- 允许连续发送多个数据包
- 消除停等协议的效率瓶颈
2.2.2.5流量控制
滑动窗口是在可靠性基础上提高效率,滑动窗口窗口越大,效率就越高,但是也不能无限大,太大了会影响到可能性,接收方的处理能力是有限的。
一、流量控制本质 通俗比喻: 接收方是水桶(缓冲区),发送方是水管。流量控制就是动态调节水龙头开度,保证水不溢出。
定义:
已发送未确认数据量 ≤ rwnd
rwnd = 接收缓冲区剩余空间
二、工作流程全景解析
阶段1:正常数据传输
接收方缓冲区:总大小64KB┌───────────────┬───────────────┐│ 已处理数据30KB │ 剩余空间34KB │ → 通告rwnd=34KB└───────────────┴───────────────┘ 发送方行为: - 连续发送34KB数据 - 等待数据确认AI写代码
阶段2:缓冲区趋近饱和
接收方状态:┌────────────────┬──────┐│ 待处理数据62KB │ 剩余2KB │ └────────────────┴──────┘处理策略: if(剩余空间 < min(MSS, 缓冲区/2)) 通告 rwnd=0 // 激活零窗口保护AI写代码
阶段3:零窗口处理(关键!)
当rwnd=0时触发特殊流程:
- 立即停止发送应用数据
- 启动**持续计时器**(默认5秒)
- 定时发送**1字节探测包**(序列号=最后字节+1)
- 若缓冲区仍满 → 回复rwnd=0
- 若缓冲区释放 → 回复最新rwnd值
示例时间线: T0: 接收方通告rwnd=0 T5: 发送方发送探测包(Seq=1001) T5: 接收方仍满 → 回复ACK=1001, rwnd=0 T10: 发送方再次探测 T10: 接收方已释放20KB → 回复ACK=1001, rwnd=20480 T10: 发送方立即发送20KB数据AI写代码
2.2.2.6拥塞控制
流量控制是依据接收方处理能力,进行限制的。(根据缓冲区的空余空间来定量衡量)
拥塞控制是依据传输链路的转发能力,进行限制的.
核心使命:在网络带宽未知的情况下,动态探测可用带宽,避免因过度发送导致全网瘫痪。
一、拥塞控制本质:网络资源的公平竞争
核心矛盾:
下文的讲解配合上面的图片食用效果更佳哦~
二、四大核心算法详解
1. 慢启动(Slow Start)
探测逻辑:指数增长快速逼近网络瓶颈
运作流程:
cwnd = 1 MSS
(约1460字节)- 到达慢启动阈值(ssthresh)
- 发生丢包(超时/重复ACK)
~
2. 拥塞避免(Congestion Avoidance)
保守增长:线性增加避免突破瓶颈
本质:每RTT增加1个MSS
~
3. 快重传(Fast Retransmit)
丢包判定:收到3个重复ACK(非超时)
~
4. 快恢复(Fast Recovery)
优化策略:丢包后避免回归慢启动
ssthresh = cwnd/2
cwnd = ssthresh + 3 MSS
(补偿重复ACK)3.拥塞控制本质总结
2.2.2.7延迟应答
默认情况下,接收方都是在收到数据报第一瞬间,就返回 ack,但是可以通过延时返回 ack 的方式来提高效率~~(即利用延时时间,赶紧消费队列中国的数据)
核心目标:减少ACK报文数量,提升网络吞吐量,同时保持TCP可靠性。
一、延迟应答的本质 技术悖论:
通俗比喻:
把ACK想象成快递签收回执:
二、核心工作原理
1. 标准ACK机制的问题
sequenceDiagram Sender->>Receiver: 数据包1 Receiver->>Sender: ACK1(立即回复) Sender->>Receiver: 数据包2 Receiver->>Sender: ACK2(立即回复) Sender->>Receiver: 数据包3 Receiver->>Sender: ACK3(立即回复)AI写代码
缺陷:ACK报文占比过高(50%带宽浪费)
2. 延迟应答的优化
sequenceDiagram Sender->>Receiver: 数据包1 Receiver->>Receiver: 启动延迟计时器(200ms) Sender->>Receiver: 数据包2 Receiver->>Receiver: 重置计时器 Sender->>Receiver: 数据包3 Receiver->>Sender: 合并ACK1+2+3(等待超时)AI写代码
优势:
三、触发条件与实现逻辑
1. 操作系统级规则
操作系统默认延迟时间最大延迟其他条件Linux40ms200ms每2个包强制ACKWindows15ms200ms收到>1个MSS时立即ACKmacOS100ms200ms窗口变化超过10%
2. 强制ACK场景(立即发送)
3. 延迟优化逻辑
if (收到新数据) { if (未启动延迟计时器) { 启动计时器(40ms); } else { 重置计时器; } if (待确认包数 >= 2) { // Linux策略 立即发送ACK; }}AI写代码
2.2.2.8捎带应答
TCP 已经有了延时应答了,基于延时应答,引入"捎带应答"。返回业务数据的时候,顺便把上次的 ack 给带回去~
如果没有延时应答,返回 ack 的时机和返回响应的时机就是不同时机~~引入了延时应答,ack可以往后延时一定时间,恰好这个时候要返回响应数据,此时就可以把 ack 也代入到响应数据中,一起返回。
一、捎带应答的本质:网络传输的"顺风车"
技术对比:
传输模式报文数量带宽利用率延迟独立ACK高(2N)低(≤70%)固定RTT/2捎带ACK低(N)高(≥95%)接近0
通俗比喻:
想象两人对话:
二、触发条件与工作原理
1. 必要条件
2. 运作流程
sequenceDiagram participant Client participant Server Client->>Server: HTTP请求(PSH,ACK Seq=100 Data="GET /") Note over Server: 生成响应数据(耗时5ms) Note over Server: 收到请求包,标记需ACK=150 Server->>Client: HTTP响应(PSH,ACK Seq=300 Ack=150 Data="200 OK")AI写代码
关键点:
3.本质总结
- 请求-响应模型优先
- 响应生成时间 < 延迟ACK超时(40ms)
- 禁用Nagle算法(`TCP_NODELAY`)避免阻塞监控命令:
2.2.2.9面向字节流(粘包问题)
我们都知道TCP有一个特点是面向字节流,那么这里就要引入一个问题,粘包问题。通过字节流方式传输,很容易混淆包和包之间的边界,从而接收方无法去区分从哪里到哪里是一个完整的应用层类数据包~
一、粘包问题的本质与定义 粘包现象是TCP协议面向字节流特性引发的特有现象,指接收方从接收缓冲区读取的数据流中,多个应用层消息的字节流粘连成无法区分的连续数据块。其本质源于两大特性:
典型表现(以客户端发送"Hello"和"World"为例):
二、解决方案
方案类型实现原理优点缺点定长协议所有消息固定长度(如512字节),不足补填充符实现简单,解析效率高浪费带宽,不适用于变长数据分隔符协议用特殊字符(如\r\n
)标记消息结尾,需转义处理兼容变长数据,直观易调试需处理内容转义,复杂度较高
在HTTP中,这俩种方案都有体现:
三、对比UDP
特性TCP粘包UDP无粘包机制数据单元无边界的字节流独立数据报(保留发送边界)协议层处理需应用层解析直接获取完整报文典型优化方向协议设计、缓冲区管理分片重组、应用层重传
自定义应用层协议,做的事情就是这个。解决粘包问题,也是咱们在自定义应用层协议的时候要考虑的问题~当然也有成熟方案,json, protobuf 都已经把粘包解决掉了~~
2.2.2.10异常情况
当然TCP在通信过程中也存在特殊情况~
一、进程崩溃场景分析 现象:当某进程崩溃时(如Java程序抛出未捕获异常),操作系统内核将接管TCP连接资源回收流程。 TCP处理流程:
应用层感知:
二、主机关机场景分析
现象:操作系统执行关机流程时,所有TCP连接进入强制关闭阶段。
TCP处理流程:
三、主机停电(掉电)场景分析
现象:物理断电导致TCP连接完全失去状态维护能力。
TCP处理机制:
四、网线断开场景分析
现象:物理链路中断导致TCP连接失去传输介质。
TCP处理机制:
3. 应用层表现:
网络恢复处理:
对标UDP和TCP就着重在传输的可靠程度与效率进行对比啦~
1. 可靠性 TCP通过确认机制、重传机制、流量控制和拥塞控制确保数据完整性和顺序,适用于对数据完整性要求高的场景(如文件传输、邮件收发)
UDP则不提供确认或重传机制,采用“尽力而为”的交付方式,可靠性较低,但适合实时性要求高的场景(如视频通话、在线游戏)
2. 高效率
UDP因无连接建立、无确认过程和更小的头部开销(仅8字节 vs TCP的20字节)而传输效率更高,延迟更低
TCP的连接管理(三次握手/四次挥手)、错误检查和重传机制增加了开销,导致传输速度较慢。
总结以下~
今天的分享到这里就结束了,喜欢的小伙伴点点赞点点关注,需要之前所有的源代码可以去我的gitee上就可以啦~你的支持就是对我最大的鼓励,大家加油!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。