TCP协议核心:TCP详细图解及TCP与UDP核心区别对比(附实战解析)

一、什么是 TCP 协议?

TCP (Transmission Control Protocol,传输控制协议) 是 OSI 模型中传输层的核心协议。

1. 核心特点(面试必背)
  • 面向连接 (Connection-Oriented):传输数据前必须先建立连接(三次握手),传输结束后释放连接(四次挥手)。
  • 可靠传输 (Reliable):保证数据无差错、不丢失、不重复、且按序到达。
  • 字节流服务 (Byte Stream):TCP 把数据看成一连串无结构的字节流,不保留报文边界(这导致了 Java 中的"粘包/拆包"问题)。
  • 全双工通信 (Full-Duplex):允许通信双方的应用进程在任何时候都能发送数据。
  • 点对点 (Point-to-Point):每条 TCP 连接只能是两个端点。
2. TCP vs UDP (简单对比)
特性 TCP UDP
连接 面向连接 无连接
可靠性 可靠 (确认、重传) 不可靠 (尽最大努力交付)
速度 较慢 (头部大、机制多) 快 (头部小、无拥塞控制)
场景 HTTP, HTTPS, SSH, 数据库连接 视频直播,DNS, 实时游戏

二、TCP 报文头格式 (TCP Header)

TCP 头部最小 20 字节 ,最大 60 字节(因为有可选字段)。理解头部字段对于使用 Wireshark 抓包分析问题至关重要。

1. 结构图解

图来自4.1 TCP 三次握手与四次挥手面试题 | 小林coding | Java面试学习

复制代码
2. 关键字段详解 (Java 后端需关注)
  1. 源端口 & 目的端口 (16 位)
    • 标识主机上的应用程序。Java 中 ServerSocket 绑定的就是目的端口,Socket 连接时会分配随机源端口。
  2. 序列号 (Seq, 32 位)
    • 作用:解决"乱序"问题。TCP 给每个字节编号,接收方根据 Seq 重组数据。
    • 初始值:连接建立时随机生成 (ISN),防止历史连接干扰。
  3. 确认号 (Ack, 32 位)
    • 作用:解决"丢失"问题。表示期望收到对方下一个报文段的第一个数据字节的序号。
    • 规则 :只有当标志位 ACK=1 时,该字段才有效。
  4. 数据偏移 (4 位)
    • 指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远(即头部长度)。单位是 4 字节,所以最大头部是 15×4=6015×4=60 字节。
  5. 标志位 (Flags, 6 位) ------ 抓包分析核心
    • URG: 紧急指针有效(很少用)。
    • ACK: 确认号有效。建立连接后所有包该位都为 1。
    • PSH (Push) : 提示接收方尽快将数据交给应用层,不要缓存(Java 中 socket.setTcpNoDelay(true) 相关)。
    • RST (Reset): 重置连接。当出现错误(如连接不存在)时,发送 RST 强行断开。
    • SYN (Synchronize) : 同步序列号。建立连接时使用(三次握手的前两次)。
    • FIN (Finish) : 释放连接。断开连接时使用(四次挥手)。
  6. 窗口 (Window, 16 位)
    • 作用:流量控制。告诉对方"我还能接收多少字节的数据"。防止发送方发太快,接收方处理不过来。
  7. 校验和 (Checksum)
    • 保证数据在传输过程中没有损坏。

三、TCP vs UDP 核心区别对比

对比维度 TCP UDP
全称 Transmission Control Protocol User Datagram Protocol
连接性 面向连接(需三次握手) 无连接(直接发送)
可靠性 可靠传输(确认+重传) 不可靠(尽最大努力交付)
数据顺序 保证顺序到达 不保证顺序
数据边界 字节流(无边界,易粘包) 数据报(保留边界)
传输速度 较慢(头部大、机制多) 快(头部小、开销少)
头部大小 最小 20 字节 固定 8 字节
流量控制 有(滑动窗口)
拥塞控制 有(慢启动、拥塞避免)
通信模式 点对点(1对1) 支持单播、广播、多播
Java 类 Socket / ServerSocket DatagramSocket / DatagramPacket

四、Java 后端开发者特别关注点

作为 Java 后端,你不需要手写 TCP 包,但你需要处理 TCP 之上的逻辑。

1. 粘包与拆包 (Sticky/Unsticky Packet)
  • 现象 :TCP 是流式协议 ,没有消息边界。
    • 粘包 :发送方发了两个包 AB,接收方一次性读到了 AB
    • 拆包 :发送方发了一个大包 A,接收方分两次读到了 A1A2
  • 原因:TCP 为了提高效率,会将小数据包合并(Nagle 算法),或者受限于 MSS (最大报文段长度) 进行拆分。
  • Java 解决方案
    • 定长:每个消息固定长度。
    • 分隔符 :如 HTTP 用 \r\n\r\n,Redis 协议用 \r\n
    • 长度字段 :在消息头加一个 int 表示 body 长度(最常用,Netty 的 LengthFieldBasedFrameDecoder
2. 心跳机制 (Keep-Alive)
  • 问题 :如果网线断了,或者对端进程挂了,TCP 连接可能一直处于 ESTABLISHED 状态(死连接)。
  • OS 级 Keepalive:默认开启但时间太长(2 小时),不适合业务。
  • 应用级心跳 :Java 后端通常自己实现。
    • 客户端每隔 30 秒发一个 PING 包。
    • 服务端收到回 PONG
    • 如果超过 90 秒没收到 PING,服务端主动关闭连接,释放资源。
    • Netty 中提供了 IdleStateHandler 轻松实现。
3. 性能调优参数 (Linux 内核)

在部署 Java 应用的高并发服务器上,通常需要调整 /etc/sysctl.conf

  • net.core.somaxconn: 监听队列最大长度(对应 ServerSocket 的 backlog 参数)。
  • net.ipv4.tcp_tw_reuse: 允许重用 TIME_WAIT 状态的 socket(解决端口耗尽)。
  • net.ipv4.tcp_keepalive_time: 调整心跳检测时间。

五、总结与建议

  1. 理解本质:TCP 是为了在不可靠的网络上构建可靠的通道,它的头部字段(Seq/Ack/Window)都是为了这个目标服务的。
  2. 工具使用 :学会使用 Wiresharktcpdump 抓包。当 Java 程序网络卡顿时,看包是 SYN 发不出去,还是 ACK 没回来,能迅速定位是网络问题还是代码问题。
  3. 框架选择
    • 简单场景:使用 java.net.Socket (BIO)。
    • 高并发场景:使用 Netty (NIO)。Netty 帮你处理了 TCP 的粘包拆包、心跳、断线重连等复杂细节,但底层原理依然是 TCP。
  4. 面试准备:重点准备三次握手、四次挥手、TIME_WAIT/CLOSE_WAIT 区别、粘包拆包解决方案。
相关推荐
linuxxx1101 小时前
让openclaw使用系统命令:“rm“, “mkdir“, “touch“, “ls“, “cat“, “echo“
linux·服务器·windows
江一破2 小时前
InfluxDB 详细介绍
数据库·influxdb
草莓熊Lotso2 小时前
MySQL 数据库基础入门:从概念到实战
linux·运维·服务器·数据库·c++·人工智能·mysql
新缸中之脑2 小时前
Pinchtab: 通过 HTTP 控制浏览器
网络·网络协议·http
芒果披萨2 小时前
linux系统管理基本命令行
linux·运维·服务器
珠海西格2 小时前
聚焦痛点|分布式光伏消纳困境的三大表现及红区治理难点
服务器·网络·分布式·安全·区块链
mingdong06082 小时前
MySQL 的mysql_secure_installation安全脚本执行过程介绍
数据库·mysql·安全
旭日跑马踏云飞2 小时前
【阿里云】扩容操作指南
服务器·阿里云·云计算
小小unicorn2 小时前
[微服务即时通讯系统]3.服务端-环境搭建
数据库·c++·redis·微服务·云原生·架构