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


目录

1.前言

2.正文

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.2.2.1确认应答

2.2.2.2超时重传

确认应答+超时重传 vs 三次握手

2.2.2.3连接管理

2.2.2.4滑动窗口

2.2.2.5流量控制

2.2.2.6拥塞控制

2.2.2.7延迟应答

2.2.2.8捎带应答

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

2.2.2.10异常情况

2.3对比UDP与TCP

3.小结


1.前言

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

2.正文

这里简单科普俩句,如果做业务开发的,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. 无连接通信

    • 无需握手过程:"即发即走"模式

    • 示例:DNS查询直接发送请求包,无需预先建立连接

  2. 不可靠传输

    • 不保证数据到达

    • 不保证顺序正确

    • 无重传机制

  3. 无拥塞控制

    • 网络拥堵时仍按原速率发送

    • 优势:避免TCP的"减速等待"问题

  4. 轻量级头部

    • 固定8字节开销(TCP至少20字节)

    • 减少网络传输负担

2.1.3理解UDP的“不可靠”

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

  1. 丢包风险:网络拥堵时路由器直接丢弃UDP包

  2. 乱序问题:后发数据可能先到

  3. 无错误修复:校验失败直接丢弃不重传

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

2.1.4面向数据报

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

# 发送端
sendto("Hello".encode())  # 发送5字节数据报
sendto("World".encode())  # 发送5字节数据报

# 接收端
data1 = recvfrom()  # 收到完整"Hello"
data2 = recvfrom()  # 收到完整"World"

核心特征

  1. 发送次数 = 接收次数

  2. 数据包大小保持不变

  3. 不存在TCP的粘包问题(粘包问题后文会详细讲解)

  4. 单次读写完整报文

2.1.5基于UDP的应用层协议

协议端口应用场景可靠性实现
DNS53域名解析应用层重试
DHCP67/68IP地址分配广播+超时重试
NTP123时间同步冗余采样
TFTP69简单文件传输块确认+重传
RTP动态实时音视频序号+时间戳
QUIC443HTTP/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:滑动窗口右移,释放缓冲区
  1. 序列号(Seq)

    • 每个字节的唯一编号(初始值随机,防攻击)

    • 例如:发送100字节数据,Seq=100 → 覆盖100~199号字节

  2. 确认号(Ack)

    • 期望收到的下一字节编号

    • Ack=200 表示199号及之前所有字节已确认收到

  3. 累积确认

    • Ack=N 意味着所有小于N的字节均已正确接收

    • 例如:收到Ack=300后,无需再确认Seq=250的包

TCP头部中实现ACK的字段:

+-+-+-+-+-+-+-+-+
| 控制标志(6位)  |
| ...ACK=1...   | → ACK标志位必须置1
+-+-+-+-+-+-+-+-+
|    确认号(32位)| → 携带Ack值(期望的下字节编号)
+-+-+-+-+-+-+-+-+

对比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. 初始发送

    • 发送数据包(如Seq=100, Len=100)

    • 同时启动重传计时器(初始RTO通常为1秒)

  2. 正常确认

    发送方->>接收方: 数据包(Seq=100)
    接收方->>发送方: ACK=200
    发送方->>发送方: 停止计时器,更新RTT
  3. 超时重传

    发送方->>接收方: 数据包(Seq=100)
    发送方->>发送方: 启动计时器(RTO=1s)
    Note over 发送方: 1秒后未收到ACK
    发送方->>接收方: 重传相同数据包
    发送方->>发送方: RTO=2s(指数退避)

确认应答+超时重传 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

    • 相当于敲门:"有人在吗?我想建立连接"

  • 二次握手:服务端回复SYN+ACK(SYN=1,ACK=1),携带随机序列号y,确认号x+1

    • 相当于回应:"我在!请确认你收到"

  • 三次握手:客户端发送ACK(ACK=1),确认号y+1

    • 相当于确认:"收到!开始通信吧"

2. 解决的问题

  • 防历史连接干扰
    若客户端发送旧SYN包(因网络延迟),服务端回应后,客户端发现序列号不匹配会发送RST终止连接

  • 双向通道验证

    • 第一次握手:服务端确认客户端发送能力正常

    • 第二次握手:客户端确认服务端收发能力正常

    • 第三次握手:服务端确认客户端接收能力正常

  • 资源合理分配
    服务端在第三次握手后才分配连接资源,避免SYN洪水攻击

3. 为什么不是两次?

4. 为什么不是四次?

  • 第三次握手已包含数据发送能力验证,额外握手增加延迟无实质收益

  • 现代TCP允许在第三次握手携带应用数据(TCP Fast Open)


四次挥手解析:

1. 核心原理

  • 首次挥手:主动方发送FIN包(FIN=1),序列号为u

    • 相当于说:"我说完了"

  • 二次挥手:被动方回复ACK确认收到

    • 相当于回应:"知道了"

  • 三次挥手:被动方发送FIN包

    • 相当于说:"我也说完了"

  • 四次挥手:主动方回复ACK确认

    • 相当于回应:"好的,再见"

2. 为什么需要四次?

  • TCP连接是全双工通道,需独立关闭两个方向

  • 关键差异:

  • 被动方需要时间处理
    收到FIN后,被动方可能还有数据要发送(如服务器需发送最后响应)

3. TIME_WAIT状态的意义

  • 持续时间:2×MSL(报文最大生存时间,通常60秒)

  • 核心作用

    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到达向右滑动


二、核心组成结构

  • 关键指针

    • 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 → 继续发送新数据


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

  1. 流量控制

    • 接收方通过窗口字段通告可用缓冲区

    • 发送方动态调整发送速率

  2. 可靠传输

    • 窗口内数据必须被确认

    • 未确认数据会重传

  3. 拥塞控制

    • 拥塞窗口(cwnd)限制最大发送量

    • 与通告窗口取最小值作为实际窗口

  4. 提升吞吐量

    • 允许连续发送多个数据包

    • 消除停等协议的效率瓶颈

2.2.2.5流量控制

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

一、流量控制本质

通俗比喻

接收方是水桶(缓冲区),发送方是水管。流量控制就是动态调节水龙头开度,保证水不溢出。

  • 水桶大小 = 接收窗口(rwnd)

  • 水流量 = 发送速率

定义

  • 接收方通过TCP头部通告接收窗口(rwnd)

  • 发送方保证:已发送未确认数据量 ≤ rwnd

  • 动态平衡点:rwnd = 接收缓冲区剩余空间


二、工作流程全景解析

阶段1:正常数据传输

接收方缓冲区:总大小64KB
┌───────────────┬───────────────┐
│ 已处理数据30KB │ 剩余空间34KB  │ → 通告rwnd=34KB
└───────────────┴───────────────┘

发送方行为:
  - 连续发送34KB数据
  - 等待数据确认

阶段2:缓冲区趋近饱和

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

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

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

  1. 发送方行为

    • 立即停止发送应用数据

    • 启动持续计时器(默认5秒)

    • 定时发送1字节探测包(序列号=最后字节+1)

  2. 接收方响应

    • 若缓冲区仍满 → 回复rwnd=0

    • 若缓冲区释放 → 回复最新rwnd值

示例时间线:
 T0: 接收方通告rwnd=0
 T5: 发送方发送探测包(Seq=1001)
 T5: 接收方仍满 → 回复ACK=1001, rwnd=0
 T10: 发送方再次探测
 T10: 接收方已释放20KB → 回复ACK=1001, rwnd=20480
 T10: 发送方立即发送20KB数据

2.2.2.6拥塞控制

流量控制是依据接收方处理能力,进行限制的。(根据缓冲区的空余空间来定量衡量)

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

核心使命:在网络带宽未知的情况下,动态探测可用带宽,避免因过度发送导致全网瘫痪。

一、拥塞控制本质:网络资源的公平竞争

核心矛盾

  • 发送方期望:尽可能占用更多带宽

  • 网络承载极限:路由器缓冲区溢出 → 全网丢包 → 吞吐量暴跌

下文的讲解配合上面的图片食用效果更佳哦~


二、四大核心算法详解

1. 慢启动(Slow Start)

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

运作流程

  1. 初始 cwnd = 1 MSS(约1460字节)

  2. 每RTT(往返时间)窗口翻倍

  3. 直到触发:

    • 到达慢启动阈值(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. 慢启动:指数探底 → 快速逼近网络瓶颈  
  • 2. 拥塞避免:线性爬坡 → 谨慎试探上限  
  • 3. 快重传/恢复:丢包应急机制 → 避免全局崩溃  
  • 4. 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(立即回复)

缺陷:ACK报文占比过高(50%带宽浪费)

2. 延迟应答的优化

sequenceDiagram
    Sender->>Receiver: 数据包1
    Receiver->>Receiver: 启动延迟计时器(200ms)
    Sender->>Receiver: 数据包2
    Receiver->>Receiver: 重置计时器
    Sender->>Receiver: 数据包3
    Receiver->>Sender: 合并ACK1+2+3(等待超时)

优势

  • ACK数量减少50%-70%

  • 允许接收方在ACK中携带更大的窗口通告


三、触发条件与实现逻辑

1. 操作系统级规则

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

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

  1. 收到乱序报文(触发快重传)

  2. 接收缓冲区满(通告窗口=0)

  3. 收到紧急数据(URG标志)

  4. 延迟计时器超时(默认40ms)

3. 延迟优化逻辑

if (收到新数据) {
    if (未启动延迟计时器) {
        启动计时器(40ms);
    } else {
        重置计时器;
    }
    if (待确认包数 >= 2) {  // Linux策略
        立即发送ACK;
    }
}

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")

关键点

  • 服务器将ACK=150 搭载 在HTTP响应报文中

  • 节省1个纯ACK包(40字节头部)


3.本质总结

  1. 捎带应答是TCP的隐形加速器,默认提升性能15%-30%

  2. 核心生效条件:双向数据流 + 延迟应答窗口

  3. 协议设计黄金法则:

    • 请求-响应模型优先

    • 响应生成时间 < 延迟ACK超时(40ms)

    • 禁用Nagle算法(TCP_NODELAY)避免阻塞

  4. 监控命令:nstat -az TcpPureAcks > tcpdump > ss -ti

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. 发送端停电
    • 对端持续发送数据触发超时重传,经历以下阶段:
  • 指数退避重传(1s, 3s, 7s, 15s...)
  • 达到tcp_retries2阈值(默认15次)后发送RST包
  • 应用层收到ECONNRESET错误
  1. 接收端停电
    • 发送端通过KeepAlive机制检测:

      KeepAlive流程:
      1. 空闲7200秒后发送探测包(默认值)
      2. 每隔75秒重试,最多9次
      3. 判定连接失效总耗时:7200 + 75*9 = 7875秒(约2小时11分)
      
    • 应用层可通过设置SO_KEEPALIVE优化检测。


四、网线断开场景分析

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

TCP处理机制

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

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上就可以啦~你的支持就是对我最大的鼓励,大家加油!

爱吃烤鸡翅的酸菜鱼 (crjs-hao) - Gitee.com https://gitee.com/crjs-hao另外最后的最后,欢迎大家加入我的社区哦,初创社区难免经验不足,请大家多多包涵,也欢迎大家前来多多交流。

爱吃烤鸡翅的酸菜鱼社区-CSDN社区云 https://bbs.csdn.net/forums/aaa1f71356f6475db42ea9ea09a392bc?spm=1001.2014.3001.6682

 

评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃烤鸡翅的酸菜鱼

希望大家对我多多支持喔~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值