首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【泼天富贵】万字长文解密UDP/TCP——手把手教你理解网络通信

【泼天富贵】万字长文解密UDP/TCP——手把手教你理解网络通信

原创
作者头像
曾高飞
发布2025-06-05 11:19:15
发布2025-06-05 11:19:15
7940
举报

1.前言

哈喽大家好呀,好久没有给大家继续带来关于Java网络原理的学习了,前一段时间网络原理的学习是大部分关于应用层的,接下来就该进入传输层的详细讲解了,今天主要给大家分享的是传输层的两大核心协议——UDP与TCP,前面学习有提及过一点点,这篇博文就给它详细讲解完。

这里简单科普俩句,如果做业务开发的,UDP/TCP更少,HTTP更多;如果做的是基础架构开发,UDP/TCP更多,HTTP更少。

2.1UDP协议

UDP:无连接,不可靠传输,面向数据包,全双工~~

2.1.1UDP协议端格式

先总体概览下:

端口号:

  • 服务器的端口是程序员指定的(提前制定好,客户端才能访问到)
  • 客户端的端口是系统自动分配的空闲端口(如果提前指定了,可能会和你客户端上的其他程序冲突)

各两个字节,共32bit位。一个端口号的取值范围,0->65535。

实际上,一般把 1024 以下的端口保留,咱们写代码都是用1024->65535 这个范围的,如果设置不在这个范围内,非法端口号。


长度:

长度由报头+载荷总长度组成。


校验和:

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

  • HTTPS 的数字签名,为了防止黑客篡改
  • UDP 的校验和,不是为了防人,和安全性无关,而是防止出现传输过程中的“比特翻转”

(光信号,电信号,电磁波,收到外界干扰可能会使高低电平/高低频光信号发生改变)

校验流程:

  • 发送之前,先计算一个校验和,把整个数据包的数据都代入
  • 把数据和校验和一起发送给对端。
  • 接收方收到之后重新计算一下校验和,和收到的校验和进行对比(UDP 发现校验和不一致,就会直接丢弃)
  • UDP 的校验和使用了 CRC 方式来进行校验 (循环冗余校验)
  • 把每个字节(除了校验和位置的部分之外),都当做整数,进行累加,溢出也没关系,继续加
  • 最终得到结果,crc 校验和
  • 传输到对端,如果数据出现错误了,对端再次计算的校验和,就会和第一个校验和不一样了~~

另外,如果两个校验和相同,原始数据一定也相同可能存在变数,这个变数即有极小概率会出现这种情况:前一个字节bit翻转刚好小了1,后一个字节bit翻转刚好大了1,最终加到一起,校验和是一样的。虽说原理上有这种情况存在,但比特翻转本身极小概率,恰好两个翻转抵消了影响,小之又小。


载荷:

就是数据本身了~

2.1.2UDP的特点

核心特点如下:

  1. 无连接通信
代码语言:actionscript
复制
- 无需握手过程:"即发即走"模式
- 示例:DNS查询直接发送请求包,无需预先建立连接不可靠传输
代码语言:actionscript
复制
- 不保证数据到达
- 不保证顺序正确
- 无重传机制
代码语言:actionscript
复制
- 网络拥堵时仍按原速率发送
- 优势:避免TCP的"减速等待"问题
代码语言:actionscript
复制
- 固定8字节开销(TCP至少20字节)
- 减少网络传输负担
2.1.3理解UDP的“不可靠”

UDP的不可靠性体现在三个层面:

  1. 丢包风险:网络拥堵时路由器直接丢弃UDP包
  2. 乱序问题:后发数据可能先到
  3. 无错误修复:校验失败直接丢弃不重传

✅ 设计哲学

UDP的"不可靠"本质是用可靠性换取性能,适合能容忍丢包的场景(如视频通话丢几帧不影响整体)

2.1.4面向数据报

与TCP的字节流不同,UDP保持应用层消息边界:

代码语言:actionscript
复制
# 发送端sendto("Hello".encode())  # 发送5字节数据报sendto("World".encode())  # 发送5字节数据报 # 接收端data1 = recvfrom()  # 收到完整"Hello"data2 = recvfrom()  # 收到完整"World"AI写代码

核心特征

  1. 发送次数 = 接收次数
  2. 数据包大小保持不变
  3. 不存在TCP的粘包问题(粘包问题后文会详细讲解)
  4. 单次读写完整报文
2.1.5基于UDP的应用层协议

协议

端口

应用场景

可靠性实现

DNS

53

域名解析

应用层重试

DHCP

67/68

IP地址分配

广播+超时重试

NTP

123

时间同步

冗余采样

TFTP

69

简单文件传输

块确认+重传

RTP

动态

实时音视频

序号+时间戳

QUIC

443

HTTP/3底层

自定义可靠传输

2.2TCP协议

TCP:有连接,面向字节流,可靠传输,全双工

2.2.1TCP协议端格式

传输层核心内容:16位源端口号+16位目的端口号 首部长度:选项的存在,导致tcp报头长度是可变的 保留:UDP 的问题,长度不够,又不能扩展~~TCP 的设计者就考虑到这样的问题。TCP 报头中就预留了一些“保留位”(现在先不用,但是占个位子)。 标志位:TCP最核心的六个标志位(里面有俩个较为少见的,所以说六个~) 16位校验和:用来校验数据是否出现错误的。


序号与确认号:

一个TCP 的载荷是多个字节构成的~~ 每个字节都分配一个编号,并且是连续递增的。序号字段填写载荷部分的第一个字节的序号,序号连续递增。

 引入序号之后,接收方就可以根据序号对数据进行排序~~这里需要引入后发先至的概念啦~TCP 需要处理后发先至的情况,确保应用程序通过 socket api 读到的数据顺序是正确的~。

TCP 在接收方这里会安排"接收缓冲区"(内存,操作系统内核里)通过网卡读到的数据,先放到接收缓冲区中,后续代码里调用read) ,也是从接受缓冲区来读的。

根据序号来排序,序号小的在前面,大的在后面确保前面的数据已经到了,然后 read 才能接触。如果是后面的数据先到,read 继续阻塞,不会读取到数据。

基于 TCP 写代码的时候,完全不必担心数据顺序的问题~(代码写起来就方便了)

如果是基于 UDP,实现拆包组包,,就需要考虑顺序,自己实现排序逻辑~~

2.2.2TCP十个核心机制

可靠性:网络通信,是非常复杂的此处的可靠性,不是说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写代码

  1. 序列号(Seq)
代码语言:actionscript
复制
- 每个字节的唯一编号(初始值随机,防攻击)
- 例如:发送100字节数据,Seq=100 → 覆盖100~199号字节
代码语言:actionscript
复制
- **期望收到的下一字节编号**
- Ack=200 表示199号及之前所有字节已确认收到
代码语言:actionscript
复制
- 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发的数据丢了
  • B->A 返回的 ack 丢了

假设当前 A ->B 发送数据,丢包的超时时间阈值为T,当 A 给 B传输发生超时之后,就会延长这个时间阈值,会继续延长这个时间,这个时间不是无休止的。超时次数达到一定程度/等待时间达到一定程度,认为网络出现严重故障,放弃这一次传输~

时间阈值怎么来的:

  • 随着进行重传,如果发现数据无法到达对方的概率越来越高。说明即使我们增加了概率,还是不能成功,意味着当前丢包概率是一个非常大的数值,意味着网络上大概率已经出现严重故障了~

俩种丢包情况:

  1. A ->B发的数据丢了
  2. B ->A 返回的 ack 丟了

对于A而言,无法分辨这俩种情况,则都是对数据进行重传,B就不一样了。在第二种情况下,B会收到两份相同的数据,这个时候TCP会在内部进行去重操作,根据序号在缓冲区寻找。

核心流程解析:

  1. 初始发送
代码语言:actionscript
复制
- 发送数据包(如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. 核心原理

  • 首次握手:客户端发送SYN包(SYN=1),携带随机序列号x
代码语言:actionscript
复制
- _相当于敲门:"有人在吗?我想建立连接"_
代码语言:actionscript
复制
- _相当于回应:"我在!请确认你收到"_
代码语言:actionscript
复制
- _相当于确认:"收到!开始通信吧"_

2. 解决的问题

  • 防历史连接干扰: 若客户端发送旧SYN包(因网络延迟),服务端回应后,客户端发现序列号不匹配会发送RST终止连接
  • 双向通道验证
代码语言:actionscript
复制
- 第一次握手:服务端确认**客户端发送能力**正常
- 第二次握手:客户端确认**服务端收发能力**正常
- 第三次握手:服务端确认**客户端接收能力**正常

3. 为什么不是两次?

4. 为什么不是四次?

  • 第三次握手已包含数据发送能力验证,额外握手增加延迟无实质收益
  • 现代TCP允许在第三次握手携带应用数据(TCP Fast Open)

四次挥手解析:

1. 核心原理

  • 首次挥手:主动方发送FIN包(FIN=1),序列号为u
代码语言:actionscript
复制
- _相当于说:"我说完了"_
代码语言:actionscript
复制
- _相当于回应:"知道了"_
代码语言:actionscript
复制
- _相当于说:"我也说完了"_
代码语言:actionscript
复制
- _相当于回应:"好的,再见"_

2. 为什么需要四次?

  • TCP连接是全双工通道,需独立关闭两个方向
  • 关键差异:
  • 被动方需要时间处理: 收到FIN后,被动方可能还有数据要发送(如服务器需发送最后响应)

3. TIME_WAIT状态的意义

  • 持续时间:2×MSL(报文最大生存时间,通常60秒)
  • 核心作用
代码语言:actionscript
复制
1. 确保最后一个ACK到达(可重传)
2. 让网络中旧报文失效(防止新连接混淆)

4. 为什么不能是三次?

  • 理论可能:被动方将ACK与FIN合并发送(实际常见)
  • 限制条件: 被动方需立即关闭时才可合并,若有数据发送仍需分开

三、核心问题解答

Q1:为什么建立连接三次,断开却要四次?

建立连接断开连接特点双方无数据传输双向通道独立关闭动作纯控制报文被动方需处理残留数据合并服务端SYN+ACK可合并被动方ACK+FIN可条件合并

Q2:握手/挥手失败如何处理?

握手失败

  • 客户端SYN无响应 → 指数退避重试(默认重试6次)
  • 服务端SYN+ACK无ACK → 重试5次(tcp_synack_retries)

挥手失败

  • 主动方FIN无ACK → 重传FIN(tcp_orphan_retries)
  • 被动方FIN无ACK → 重传FIN(tcp_max_orphans)

2.2.2.4滑动窗口

想必大家听到这个词也是耳熟能详了,算法当中滑动窗口可是鼎鼎大名,但事实上算法上的滑动窗口就是来源于TCP中的~,因为TCP在保证可靠性的时候,付出了效率的代价,所以滑动窗口的设计就是为了提高点效率~

一、滑动窗口是什么?

通俗比喻

把网络想象成一条流水线,滑动窗口就是允许连续作业的区域

  • 窗口大小 = 流水线可容纳的未完成品数量
  • ACK到达 = 完成品离开流水线,新原料可加入

技术定义

  • 发送方维护的连续发送数据范围
  • 窗口内的数据可无需确认直接发送
  • 窗口随ACK到达向右滑动

二、核心组成结构

  • 关键指针
代码语言:actionscript
复制
- `SND.UNA`:滑动窗口左边界(Send Unacknowledged)
- `SND.NXT`:下一个要发送的数据位置(Send Next)
- **窗口大小** = `WIN.END - SND.UNA`(动态变化)
代码语言:actionscript
复制
1. 初始状态:窗口覆盖字节1-200
2. 发送字节1-100 → 进入**已发未确认**区域
3. 收到ACK=101 → 窗口**向右滑动**,101-200变为可发送区
4. 新窗口覆盖101-300 → 继续发送新数据

三、滑动窗口四大核心作用

  1. 流量控制
代码语言:actionscript
复制
- 接收方通过窗口字段通告可用缓冲区
- 发送方动态调整发送速率
代码语言:actionscript
复制
- 窗口内数据必须被确认
- 未确认数据会重传
代码语言:actionscript
复制
- 拥塞窗口(cwnd)限制最大发送量
- 与通告窗口取最小值作为实际窗口
代码语言:actionscript
复制
- 允许连续发送多个数据包
- 消除停等协议的效率瓶颈

2.2.2.5流量控制

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

一、流量控制本质 通俗比喻: 接收方是水桶(缓冲区),发送方是水管。流量控制就是动态调节水龙头开度,保证水不溢出。

  • 水桶大小 = 接收窗口(rwnd)
  • 水流量 = 发送速率

定义

  • 接收方通过TCP头部通告接收窗口(rwnd)
  • 发送方保证:已发送未确认数据量 ≤ rwnd
  • 动态平衡点:rwnd = 接收缓冲区剩余空间

二、工作流程全景解析

阶段1:正常数据传输

接收方缓冲区:总大小64KB┌───────────────┬───────────────┐│ 已处理数据30KB │ 剩余空间34KB │ → 通告rwnd=34KB└───────────────┴───────────────┘ 发送方行为: - 连续发送34KB数据 - 等待数据确认AI写代码

阶段2:缓冲区趋近饱和

接收方状态:┌────────────────┬──────┐│ 待处理数据62KB │ 剩余2KB │ └────────────────┴──────┘处理策略: if(剩余空间 < min(MSS, 缓冲区/2)) 通告 rwnd=0 // 激活零窗口保护AI写代码

阶段3:零窗口处理(关键!)

当rwnd=0时触发特殊流程:

  1. 发送方行为
代码语言:actionscript
复制
- 立即停止发送应用数据
- 启动**持续计时器**(默认5秒)
- 定时发送**1字节探测包**(序列号=最后字节+1)
代码语言:actionscript
复制
- 若缓冲区仍满 → 回复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)

探测逻辑:指数增长快速逼近网络瓶颈

运作流程

  1. 初始 cwnd = 1 MSS(约1460字节)
  2. 每RTT(往返时间)窗口翻倍
  3. 直到触发:
代码语言:actionscript
复制
- 到达慢启动阈值(ssthresh)
- 发生丢包(超时/重复ACK)

~

2. 拥塞避免(Congestion Avoidance)

保守增长:线性增加避免突破瓶颈

本质:每RTT增加1个MSS

  • RTT内收到N个ACK → 每个ACK增加 1/N MSS

~

3. 快重传(Fast Retransmit)

丢包判定:收到3个重复ACK(非超时)

~

4. 快恢复(Fast Recovery)

优化策略:丢包后避免回归慢启动

  1. 重传丢失包(快重传触发)
  2. 设置 ssthresh = cwnd/2
  3. cwnd = ssthresh + 3 MSS(补偿重复ACK)
  4. 进入拥塞避免阶段

3.拥塞控制本质总结

  1. 慢启动:指数探底 → 快速逼近网络瓶颈  

    1. 拥塞避免:线性爬坡 → 谨慎试探上限  
    1. 快重传/恢复:丢包应急机制 → 避免全局崩溃  
    1. BBR革命:基于模型而非启发 → 直接控制带宽与时延  

2.2.2.7延迟应答

默认情况下,接收方都是在收到数据报第一瞬间,就返回 ack,但是可以通过延时返回 ack 的方式来提高效率~~(即利用延时时间,赶紧消费队列中国的数据)

核心目标:减少ACK报文数量,提升网络吞吐量,同时保持TCP可靠性。

一、延迟应答的本质 技术悖论

  • 传统模式:每收到一个数据包立即回复ACK → 可靠性高但效率低
  • 延迟应答:短暂等待后再回复ACK → 减少报文数量,提升有效带宽

通俗比喻

把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写代码

优势

  • ACK数量减少50%-70%
  • 允许接收方在ACK中携带更大的窗口通告

三、触发条件与实现逻辑

1. 操作系统级规则

操作系统默认延迟时间最大延迟其他条件Linux40ms200ms每2个包强制ACKWindows15ms200ms收到>1个MSS时立即ACKmacOS100ms200ms窗口变化超过10%

2. 强制ACK场景(立即发送)

  1. 收到乱序报文(触发快重传)
  2. 接收缓冲区满(通告窗口=0)
  3. 收到紧急数据(URG标志)
  4. 延迟计时器超时(默认40ms)

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

通俗比喻

想象两人对话:

  • 独立ACK:A说"吃了吗?" → B回"收到了" → B再说"吃过了"(冗余确认)
  • 捎带ACK:A说"吃了吗?" → B直接回"吃过了"(隐含确认)

二、触发条件与工作原理

1. 必要条件

  • 双向数据流:通信双方同时存在数据传输需求
  • 时间窗口匹配:ACK生成时,反向数据正在准备发送
  • 延迟应答启用:为捎带创造时间窗口(通常40ms内)

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写代码

关键点

  • 服务器将ACK=150 搭载 在HTTP响应报文中
  • 节省1个纯ACK包(40字节头部)

3.本质总结

  1. 捎带应答是TCP的隐形加速器,默认提升性能15%-30%
  2. 核心生效条件:双向数据流 + 延迟应答窗口
  3. 协议设计黄金法则:
代码语言:actionscript
复制
- 请求-响应模型优先
- 响应生成时间 < 延迟ACK超时(40ms)
- 禁用Nagle算法(`TCP_NODELAY`)避免阻塞监控命令:

2.2.2.9面向字节流(粘包问题)

我们都知道TCP有一个特点是面向字节流,那么这里就要引入一个问题,粘包问题。通过字节流方式传输,很容易混淆包和包之间的边界,从而接收方无法去区分从哪里到哪里是一个完整的应用层类数据包~

一、粘包问题的本质与定义 粘包现象是TCP协议面向字节流特性引发的特有现象,指接收方从接收缓冲区读取的数据流中,多个应用层消息的字节流粘连成无法区分的连续数据块。其本质源于两大特性:

  1. 无消息边界:TCP将数据视为连续的字节流,不维护应用层消息的起始与终止标识
  2. 动态分段机制:TCP根据网络状况(MTU、MSS、滑动窗口)自动切割/合并字节流,与应用程序的写入/读取操作无关

典型表现(以客户端发送"Hello"和"World"为例):

  • 理想情况:接收端分两次读取"Hello"和"World"
  • 粘包情况:接收端一次性读取"HelloWorld"(正向粘包)或分次读取"Hel"+"loWorld"(边界错位)

二、解决方案

方案类型实现原理优点缺点定长协议所有消息固定长度(如512字节),不足补填充符实现简单,解析效率高浪费带宽,不适用于变长数据分隔符协议用特殊字符(如\r\n)标记消息结尾,需转义处理兼容变长数据,直观易调试需处理内容转义,复杂度较高

在HTTP中,这俩种方案都有体现:

  • 1.GET 请求,,没有 body,使用空行,作为结束标记
  • 2.POST 请求,有 body 的时候,通过 Content-Length 决定 body 多长~~

三、对比UDP

特性TCP粘包UDP无粘包机制数据单元无边界的字节流独立数据报(保留发送边界)协议层处理需应用层解析直接获取完整报文典型优化方向协议设计、缓冲区管理分片重组、应用层重传

自定义应用层协议,做的事情就是这个。解决粘包问题,也是咱们在自定义应用层协议的时候要考虑的问题~当然也有成熟方案,json, protobuf 都已经把粘包解决掉了~~ 

2.2.2.10异常情况

当然TCP在通信过程中也存在特殊情况~

一、进程崩溃场景分析 现象:当某进程崩溃时(如Java程序抛出未捕获异常),操作系统内核将接管TCP连接资源回收流程。 TCP处理流程

  1. 资源回收:内核立即回收进程的PCB(进程控制块),关闭文件描述符表中的Socket资源。
  2. 四次挥手触发
    • 若进程崩溃时连接处于ESTABLISHED状态,内核自动发送FIN报文启动四次挥手流程。
    • 即使进程已终止,内核仍能完成FIN-ACK交换,保证连接正常关闭。
  3. 特殊场景处理
    • 若进程崩溃时存在未发送数据,内核缓冲区中的数据仍会继续传输(延迟关闭机制)。
    • 若接收方在FIN到达前已发送数据,将触发TCP重置机制(RST包)。

应用层感知

  • 对端应用会立即收到EOF(End Of File)信号,read()返回0值。
  • 若对端正在发送数据,可能触发ECONNRESET错误(连接被重置)。

二、主机关机场景分析

现象:操作系统执行关机流程时,所有TCP连接进入强制关闭阶段。

TCP处理流程

  1. 进程终止阶段
    • Init进程发送SIGTERM信号给所有进程,等待5秒后发送SIGKILL。
    • 存活进程有机会发送FIN包完成四次挥手(如数据库事务回滚)。
  2. 内核级关闭
    • 未完成挥手的连接进入TIME_WAIT状态(2*MSL时间,默认60秒)。
    • 关机前未发送的ACK包可能导致对端超时重传(重试次数由tcp_retries2控制)。
  3. 异常场景
    • 强制关机(长按电源键)等效于停电场景处理。
    • 虚拟化环境中可能触发TCP连接迁移(如VMware vMotion)。

三、主机停电(掉电)场景分析

现象:物理断电导致TCP连接完全失去状态维护能力。

TCP处理机制

  1. 发送端停电
    • 对端持续发送数据触发超时重传,经历以下阶段:
  2. 指数退避重传(1s, 3s, 7s, 15s...)
  3. 达到tcp_retries2阈值(默认15次)后发送RST包
  4. 应用层收到ECONNRESET错误
  5. 接收端停电: - 发送端通过KeepAlive机制检测: KeepAlive流程:1. 空闲7200秒后发送探测包(默认值)2. 每隔75秒重试,最多9次3. 判定连接失效总耗时:7200 + 75*9 = 7875秒(约2小时11分)AI写代码 - 应用层可通过设置SO_KEEPALIVE优化检测。

四、网线断开场景分析

现象:物理链路中断导致TCP连接失去传输介质。

TCP处理机制

  1. 立即检测型断开
    • 交换机端口状态变化触发TCP RST包(需开启LLDP/CDP协议)
    • 路由协议更新导致连接重置(OSPF/BGP收敛时间影响)
  2. 静默断开检测
    • 发送端通过以下机制感知:连续发送3个KeepAlive探测包无响应根据RTO(Retransmission Timeout)计算重传超时:math RTO = SRTT + max(G, 4*RTTVAR) AI写代码

3. 应用层表现

  • 出现"Network is unreachable"或"Host unreachable"错误
  • 数据库连接池进入连接熔断状态

网络恢复处理

  1. 短时断开(<RTO时间):TCP自动恢复,应用无感知
  2. 长时断开:
    • 应用需实现重连机制(如指数退避算法)
    • 使用TCP持久化特性(RFC 5482)防止路由表过期

2.3对比UDP与TCP

对标UDP和TCP就着重在传输的可靠程度与效率进行对比啦~

1. 可靠性 TCP通过确认机制重传机制流量控制拥塞控制确保数据完整性和顺序,适用于对数据完整性要求高的场景(如文件传输、邮件收发)

UDP则不提供确认或重传机制,采用“尽力而为”的交付方式,可靠性较低,但适合实时性要求高的场景(如视频通话、在线游戏)


2. 高效率

UDP因无连接建立无确认过程更小的头部开销(仅8字节 vs TCP的20字节)而传输效率更高,延迟更低

TCP的连接管理(三次握手/四次挥手)、错误检查和重传机制增加了开销,导致传输速度较慢。

总结以下~

  • 可靠传输:TCP更优,适合数据完整性优先的场景。
  • 高效率:UDP更优,适合实时性要求高的场景。

3.小结

今天的分享到这里就结束了,喜欢的小伙伴点点赞点点关注,需要之前所有的源代码可以去我的gitee上就可以啦~你的支持就是对我最大的鼓励,大家加油!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2.1UDP协议
    • 2.1.1UDP协议端格式
    • 2.1.2UDP的特点
    • 2.1.3理解UDP的“不可靠”
    • 2.1.4面向数据报
    • 2.1.5基于UDP的应用层协议
  • 2.2TCP协议
    • 2.2.1TCP协议端格式
    • 2.2.2TCP十个核心机制
  • 2.3对比UDP与TCP
  • 3.小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档