TCP与UDP实战:从握手到黏包解决
·
文章目录
目录
前言
本文章主要解决TCP的常见面试问题
一、TCP的三次握手,四次挥手
1.三次握手(建立可靠连接)
核心目的:同步双方序列号 + 确认收发能力,确保连接可双向通信。

- 第一次握手(客户端→服务器):客户端发送
SYN报文(同步序列编号),告知服务器 “我要连接,初始序号是 X”,客户端进入SYN_SENT状态。 - 第二次握手(服务器→客户端):服务器回应
SYN+ACK报文,确认 “收到 X,我的初始序号是 Y”,服务器进入SYN_RCVD状态。 - 第三次握手(客户端→服务器):客户端发送
ACK报文,确认 “收到 Y,连接可以建立”,双方进入ESTABLISHED状态(数据传输状态)。
2.四次挥手(终止可靠连接)
核心目的:双方协商关闭连接,确保所有数据传输完成。

- 第一次挥手(主动关闭方→被动关闭方):主动方发送
FIN报文(结束标志),告知 “我已无数据要发”,进入FIN_WAIT_1状态。 - 第二次挥手(被动关闭方→主动关闭方):被动方回应
ACK报文,确认 “收到关闭请求”,进入CLOSE_WAIT状态(此时被动方仍可发送剩余数据)。 - 第三次挥手(被动关闭方→主动关闭方):被动方数据发送完毕后,发送
FIN报文,告知 “我也无数据要发”,进入LAST_ACK状态。 - 第四次挥手(主动关闭方→被动关闭方):主动方回应
ACK报文,确认 “收到关闭请求”,进入TIME_WAIT状态(等待 2MSL,确保被动方收到 ACK),双方最终进入CLOSED状态。
3.为什么是三次和四次
1.为什么是三次
两次握手无法确认客户端的接收能力(服务器无法判断自己的 SYN+ACK 是否被收到),三次才能确保双向通信链路通畅。
2.为什么是四次
- 关闭连接时,被动方可能仍有数据未发送,需分 “确认关闭请求” 和 “自己发起关闭” 两步,无法像三次握手那样合并
SYN+ACK。 TIME_WAIT作用:避免旧连接的残留报文干扰新连接,确保被动方收到最后一个 ACK。
补充:四次挥手中的2MSL
2MSL 核心定义与作用
2MSL 是 Two Maximum Segment Lifetime 的缩写,即两倍的报文最大生存时间,是 TCP 协议中 TIME_WAIT 状态的等待时长标准。
- MSL 定义:指 TCP 报文在网络中能存在的最长时间(避免报文因网络延迟、路由环路无限循环转发),典型值为 30 秒、1 分钟或 2 分钟(不同系统配置不同,常见 1 分钟)。
- 2MSL 核心作用(面试必考点):
- 确保主动关闭方发送的最后一个 ACK 报文能被被动关闭方收到(若被动方没收到 FIN,会重发 FIN,2MSL 足够覆盖重发周期)。
- 等待网络中该连接的所有残留报文过期(防止旧报文干扰后续新建的同端口连接)。
简单说:2MSL 是 TCP 为了 “可靠关闭连接” 和 “避免旧连接干扰” 设置的 “安全等待时间”,TIME_WAIT 状态结束后,连接才会真正进入 CLOSED 状态。
二、TCP和UDP具体应用
1.TCP 与 UDP 核心特性对比(应用选择的关键)
| 特性 | TCP(传输控制协议) | UDP(用户数据报协议) |
|---|---|---|
| 连接性 | 面向连接(三次握手建立,四次挥手关闭) | 无连接(直接发送,无需建立连接) |
| 可靠性 | 可靠(重传、流量控制、拥塞控制) | 不可靠(无重传,可能丢失、乱序) |
| 传输效率 | 较低(头部开销大,有确认 / 重传机制) | 较高(头部仅 8 字节,无额外开销) |
| 适用场景 | 需确保数据完整、有序的场景 | 实时性优先,可容忍少量数据丢失的场景 |
2.具体应用场景
1. TCP 的典型应用(可靠优先)
- 网页浏览(HTTP/HTTPS):需完整接收 HTML、图片等数据,避免页面错乱,HTTPS 基于 TCP 实现加密传输。
- 文件传输(FTP/SFTP):大文件传输需保证无丢失、无损坏,TCP 的重传机制可避免文件残缺。
- 邮件发送(SMTP/POP3/IMAP):邮件内容、附件需准确送达,不允许丢失或乱序。
- 远程登录(SSH/Telnet):远程操作设备需指令有序执行,TCP 确保指令传输可靠。
- 数据库连接(MySQL/PostgreSQL):数据库查询、写入需原子性,TCP 避免数据传输错误导致事务异常。
- 即时通讯(微信 / QQ 私聊文字):文字消息需准确送达,丢失会影响沟通,语音 / 视频则常用 UDP。
2. UDP 的典型应用(实时性优先)
- 实时音视频(直播 / 视频通话 / 语音聊天):如抖音直播、Zoom 会议,低延迟比偶尔的数据包丢失更重要,丢失少量帧不影响整体体验。
- 游戏联机(王者荣耀 / 英雄联盟):游戏操作指令需实时响应(如走位、放技能),UDP 的低延迟可避免操作卡顿,少量丢包可通过游戏算法补偿。
- DNS 查询:域名解析请求小(仅几十字节),需快速响应,即使丢包也可重新发送,无需 TCP 的连接开销。
- 流媒体传输(RTSP/RTP):视频点播、直播的音视频流,优先保证流畅播放,延迟过高会影响体验。
- 物联网通信(MQTT 轻量化场景):传感器数据上报(如温度、湿度),数据量小、实时性要求高,可容忍偶尔丢包。
3.核心选择原则
- 若数据 “不能丢、不能乱”(如文件、文字、数据库操作),选 TCP;
- 若 “延迟要低、效率要高”(如音视频、游戏、实时数据),选 UDP,可通过应用层实现简单重传(如 RTP 协议封装音视频数据)。
三、TCP黏包问题如何确定边界
TCP 黏包的本质是 “流式协议无天然边界”,数据按字节流传输,接收方无法区分多个发送方的数据包。解决核心是 在应用层定义 “边界规则”,让接收方能够准确拆分黏包数据。
4 种高频边界确定方法
1. 固定长度法(最简单,局限性强)
- 核心逻辑:约定每个数据包的固定字节数,接收方每次读取固定长度数据,即为一个完整包。
- 实现方式:例如约定每个包 100 字节,发送方每次发送 100 字节数据,接收方循环读取 100 字节即可拆分。
- 优缺点:优点是实现简单,无需额外解析;缺点是灵活性差,数据长度不固定时会浪费带宽(不足补 0)或截断数据。
- 适用场景:数据长度固定的场景(如传感器固定格式上报数据)。
2. 分隔符法(灵活,需避坑)
- 核心逻辑:约定特殊分隔符(如
\r\n、自定义字节序列0xFFFF),接收方读取数据时,以分隔符作为包的结束标志。 - 实现方式:发送方在每个数据包末尾添加分隔符(如 HTTP 协议的
\r\n\r\n分隔请求头和正文);接收方缓冲区累积数据,直到匹配到分隔符,拆分出完整包。 - 关键避坑:需确保数据本身不包含分隔符(否则会误拆分),解决方案是 “转义处理”(如数据中的
\r\n转义为\r\n\0)。 - 优缺点:优点是灵活,无需提前约定长度;缺点是需处理分隔符冲突,解析效率略低。
- 适用场景:文本协议(如日志传输、简单指令交互)。
3. 长度字段法(最常用,通用性强)
- 核心逻辑:每个数据包分为 “头部 + 数据体”,头部固定长度(如 2 字节 / 4 字节),存储数据体的字节数,接收方先读头部获取长度,再按长度读取数据体。
- 实现方式(C++ 示例):
// 发送方:打包(头部2字节存储数据长度)
std::string data = "hello tcp";
uint16_t len = htons(data.size()); // 网络字节序(大端)
send(sockfd, &len, sizeof(len), 0); // 先发送长度
send(sockfd, data.c_str(), data.size(), 0); // 再发送数据
// 接收方:拆包
uint16_t len;
recv(sockfd, &len, sizeof(len), 0); // 先读长度
len = ntohs(len); // 转换为主机字节序
char buf[len];
recv(sockfd, buf, len, 0); // 按长度读数据体
- 关键注意:头部长度需固定,且要处理 “网络字节序(大端)” 与 “主机字节序(小端)” 的转换(
htons/ntohs函数)。 - 优缺点:优点是通用性强、解析高效,无分隔符冲突问题;缺点是需额外处理头部,略增加开发量。
- 适用场景:绝大多数场景(如即时通讯、文件传输、自定义二进制协议),是面试高频考点。
4. 应用层协议封装法(标准化,适合复杂场景)
- 核心逻辑:基于成熟协议(如 Protobuf、JSON、XML)或自定义结构化协议,通过协议本身的格式定义边界。
- 实现方式:
- 结构化协议(如 Protobuf):数据按字段编码,包含字段类型和长度,接收方按协议格式解析即可自动拆分;
- 文本协议(如 JSON):以
{}或[]作为逻辑边界,接收方解析 JSON 结构完成拆包。
- 优缺点:优点是标准化、可扩展性强,支持复杂数据结构;缺点是解析开销略大。
- 适用场景:分布式系统、跨平台通信、复杂数据交互(如微服务接口、客户端 - 服务器数据传输)。
核心原则与面试总结
- 边界确定的本质是 “应用层协议约定”,TCP 本身不提供边界,需通过上述方法手动定义;
- 面试优先选 长度字段法 作答(通用性强,能体现技术细节),需说明 “头部长度 + 网络字节序转换 + 数据体读取” 的完整流程;
- 实际开发中,优先使用成熟协议(如 Protobuf)或成熟框架(如 Boost.Asio)的拆包机制,避免重复造轮子。
更多推荐



所有评论(0)