4、传输层协议:TCP协议特点与报文段结构、三次握手与四次挥手、滑动窗口与流量控制、UDP协议特点与应用场景

传输层,说白了就是给应用层的数据包「贴标签」和「做服务」的。你想想看,网络层只负责把数据从一个机器送到另一个机器,但机器上跑着那么多进程,数据该给谁?这就是传输层要解决的问题。

我个人习惯把传输层比作快递公司。TCP 是顺丰,可靠但慢一点;UDP 是普通快递,快但可能丢件。做嵌入式开发,选哪个协议,直接决定了你产品的稳定性和实时性。

TCP 协议:可靠传输的基石

TCP 的全称是 Transmission Control Protocol,传输控制协议。它的核心就两个字:可靠

我在项目中遇到过很多次,刚毕业的工程师觉得 TCP 太复杂,想用 UDP 自己加重传机制。结果呢?项目延期,bug 不断。TCP 的可靠性是经过几十年验证的,别轻易自己造轮子。

TCP 的特点

  • 面向连接:通信前必须先建立连接,通信结束后释放连接。
  • 全双工通信:双方可以同时发送和接收数据。
  • 可靠传输:无差错、不丢失、不重复、按序到达。
  • 流量控制:根据接收方的能力调整发送速度。
  • 拥塞控制:根据网络状况调整发送速度。

核心要点:TCP 的可靠性不是免费的午餐。它需要额外的头部开销(20字节),需要三次握手的延迟,需要维护状态机。在资源受限的嵌入式设备上,这些成本必须考虑进去。

TCP 报文段结构

TCP 报文段的结构,我建议你死记硬背也要记住。因为调试网络问题时,抓包分析全靠它。

字段 长度 说明
源端口 16位 发送方的端口号
目的端口 16位 接收方的端口号
序号 32位 本报文段数据的第一个字节的序号
确认号 32位 期望收到对方下一个报文段的序号
数据偏移 4位 TCP头部长度,单位是32位字
标志位 6位 URG、ACK、PSH、RST、SYN、FIN
窗口 16位 接收窗口大小,用于流量控制
校验和 16位 检验整个TCP报文段
紧急指针 16位 紧急数据的结束位置

嗯,这里要注意:序号和确认号是 TCP 可靠传输的核心。序号不是报文段的编号,而是字节的编号。比如你发送 1000 字节的数据,序号是 100,那下一个报文段的序号就是 1100。

三次握手:建立连接

为什么是三次?不是两次,也不是四次?

我曾经在面试时被这个问题问住过。后来想明白了:三次握手是为了防止「已失效的连接请求报文段」突然又传到服务器。

流程很简单:

  1. 客户端发送 SYN:客户端发送 SYN=1,seq=x 的报文段,进入 SYN_SENT 状态。
  2. 服务器回复 SYN+ACK:服务器收到后,发送 SYN=1,ACK=1,seq=y,ack=x+1 的报文段,进入 SYN_RCVD 状态。
  3. 客户端发送 ACK:客户端收到后,发送 ACK=1,seq=x+1,ack=y+1 的报文段,进入 ESTABLISHED 状态。服务器收到后也进入 ESTABLISHED 状态。

实战经验:在嵌入式设备上,我建议把 SYN 超时重传时间设短一点。默认是 1 秒,但在局域网内,200 毫秒就够。这样可以加快连接建立速度。

四次挥手:释放连接

为什么挥手要四次?因为 TCP 是全双工的,每一方的连接都需要单独关闭。

  1. 客户端发送 FIN:客户端发送 FIN=1,seq=u 的报文段,进入 FIN_WAIT_1 状态。
  2. 服务器回复 ACK:服务器收到后,发送 ACK=1,seq=v,ack=u+1,进入 CLOSE_WAIT 状态。客户端收到后进入 FIN_WAIT_2 状态。
  3. 服务器发送 FIN:服务器数据发送完毕后,发送 FIN=1,ACK=1,seq=w,ack=u+1,进入 LAST_ACK 状态。
  4. 客户端回复 ACK:客户端收到后,发送 ACK=1,seq=u+1,ack=w+1,进入 TIME_WAIT 状态。等待 2MSL 后关闭。

避坑指南:我曾经在做一个物联网网关项目时,发现设备频繁断连。抓包一看,TIME_WAIT 状态堆积了上千个。原因是客户端主动关闭连接后,没有等 2MSL 就重新连接了。解决方案是让服务器主动关闭连接,或者缩短 TIME_WAIT 时间。

滑动窗口与流量控制

滑动窗口,说白了就是接收方告诉发送方:「你一次最多发这么多,别撑着我。」

接收方会在 TCP 头部的「窗口」字段中填入自己的接收缓冲区剩余大小。发送方根据这个值调整自己的发送窗口。

举个例子:

  • 接收方窗口大小是 1000 字节
  • 发送方发送了 500 字节,收到 ACK 确认了 300 字节
  • 那么发送窗口向前滑动 300 字节,还可以发送 800 字节

我建议你在嵌入式设备上实现 TCP 时,注意窗口大小的设置。太小了影响吞吐量,太大了浪费内存。一般设置为 MSS(最大报文段长度)的 2-4 倍比较合适。

UDP 协议:简单高效的传输

UDP,User Datagram Protocol,用户数据报协议。它和 TCP 完全相反:无连接、不可靠、尽最大努力交付

UDP 的报文段结构简单得令人发指:

字段 长度 说明
源端口 16位 发送方的端口号
目的端口 16位 接收方的端口号
长度 16位 UDP报文段的总长度
校验和 16位 检验整个UDP报文段,可选

只有 8 字节的头部!相比 TCP 的 20 字节,省了一半多。

UDP 的应用场景

UDP 适合以下场景:

  • 实时性要求高:比如视频通话、在线游戏。丢一帧画面没关系,但延迟不能高。
  • 广播/多播:UDP 支持一对多通信,TCP 不支持。
  • 简单查询:比如 DNS 查询,一个请求一个响应,用 TCP 太浪费。
  • 资源受限设备:嵌入式设备内存小,跑 TCP 状态机可能吃不消。

我的建议:在嵌入式项目中,如果数据量小、实时性要求高、可以容忍偶尔丢包,果断用 UDP。比如传感器数据采集,每秒上报一次温度值,丢一两个包完全不影响。

TCP vs UDP:如何选择?

我经常被问到这个问题。其实很简单:

  • 需要可靠传输?选 TCP。
  • 需要实时性?选 UDP。
  • 不确定?先选 TCP,性能不够再优化。

但要注意,UDP 也可以实现可靠传输,只需要在应用层加确认和重传机制。比如 QUIC 协议就是在 UDP 上实现的可靠传输。不过,这需要你投入额外的开发成本。

最后说一句:传输层的选择没有银弹。我见过用 TCP 做视频传输的,也见过用 UDP 做文件传输的。关键是要理解你的业务需求,然后做出权衡。