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

0 阅读5分钟

 一、什么是 TCP 协议?

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

1. 核心特点(面试必背)

  • 面向连接 (Connection-Oriented) :传输数据前必须先建立连接(三次握手),传输结束后释放连接(四次挥手)。
  • 可靠传输 (Reliable) :保证数据无差错、不丢失、不重复、且按序到达。
  • 字节流服务 (Byte Stream) :TCP 把数据看成一连串无结构的字节流,不保留报文边界(这导致了 Java 中的“粘包/拆包”问题)。
  • 全双工通信 (Full-Duplex) :允许通信双方的应用进程在任何时候都能发送数据。
  • 点对点 (Point-to-Point) :每条 TCP 连接只能是两个端点。

2. TCP vs UDP (简单对比)

特性TCPUDP
连接面向连接无连接
可靠性可靠 (确认、重传)不可靠 (尽最大努力交付)
速度较慢 (头部大、机制多)快 (头部小、无拥塞控制)
场景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 核心区别对比

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