2. UDP协议详解:报文格式、端口号、校验和计算、UDP与TCP的对比

各位同学,今天我们聊聊UDP协议。说实话,UDP在FPGA网络通信里太常用了。我最早接触它的时候,总觉得它太“简陋”——连个握手都没有,数据发出去就不管了。但后来做项目多了才发现,正是这种“简陋”,让它在很多场景下无可替代。

2.1 UDP报文格式——简单到令人发指

UDP的报文结构,说白了就是“短小精悍”。它只有8个字节的头部,加上数据部分。你想想看,TCP头部至少20字节,UDP直接省了一半多。

来看一下UDP报文的完整结构:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         源端口号(16bit)        |       目的端口号(16bit)       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          UDP长度(16bit)        |        UDP校验和(16bit)      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        数据(可变长度)                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

每个字段的含义:

  • 源端口号(16bit):发送方的端口。如果不需要回复,可以填0。我在项目中经常这么干——单向数据采集,根本不需要对方回数据,源端口直接写0,省事。
  • 目的端口号(16bit):接收方的端口。这个必须填,不然数据到了对方那里,系统不知道交给哪个应用程序。
  • UDP长度(16bit):整个UDP报文(头部+数据)的字节数。最小值是8(只有头部,没有数据)。
  • UDP校验和(16bit):用于检测数据在传输过程中有没有出错。注意,这个字段是可选的——在IPv4里可以全填0,表示不校验。但我建议你千万别偷懒,后面我会讲为什么。

重点记忆:UDP头部固定8字节,数据长度可变。在FPGA里解析时,直接按字节偏移取字段就行,非常方便。

2.2 端口号概念——应用程序的“门牌号”

端口号这个概念,我刚开始学的时候总觉得有点抽象。后来我打了个比方就明白了:IP地址是找哪台电脑,端口号是找电脑上的哪个程序

举个例子:你的电脑上同时开着微信和浏览器,它们都在收发网络数据。数据包到了你的电脑,系统怎么知道这个包是给微信的,还是给浏览器的?靠的就是端口号。

端口号的范围是0~65535,分为三类:

分类 范围 说明
知名端口 0~1023 系统预留,比如HTTP用80,DNS用53
注册端口 1024~49151 用户应用程序可用,但需要申请
动态/私有端口 49152~65535 临时使用,客户端一般用这个范围

我的经验:在FPGA做UDP通信时,端口号建议选1024以上的。我曾经图省事用了8080,结果和电脑上某个服务冲突了,排查了半天才发现是端口被占用了。从那以后,我习惯用5000~8000之间的端口,基本不会撞车。

2.3 校验和计算——看似复杂,其实有套路

UDP校验和的计算,很多初学者觉得头疼。其实说白了,就是二进制反码求和。我当年第一次手算校验和时,算错了好几次,后来总结了一个口诀:“逐位相加,溢出回卷,最后取反”。

校验和的计算范围比较特殊——它不光算UDP头部和数据,还加了一个伪头部。伪头部不是真正发送的数据,只是用来校验IP地址和协议类型是否正确。

伪头部的结构:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        源IP地址(32bit)                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        目的IP地址(32bit)                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x00      |     协议号(0x11)  |       UDP长度(16bit)     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

计算步骤:

  1. 把伪头部、UDP头部、UDP数据拼在一起
  2. 如果数据长度不是偶数,末尾补一个0x00字节
  3. 把所有16bit字做二进制反码求和
  4. 把结果取反,填入校验和字段

注意:在FPGA里实现校验和计算时,我踩过一个坑——字节序问题。网络字节序是大端模式,而FPGA内部处理时很容易搞反。我曾经因为这个问题,调了整整两天才发现是高低字节颠倒了。所以,写代码时一定要确认好字节序。

2.4 UDP与TCP的对比——各有各的活法

UDP和TCP,就像快递里的“普通包裹”和“顺丰特快”。TCP保证送到,但慢;UDP不保证送到,但快。在FPGA里,这个区别尤其明显。

我画了一张对比图,帮你快速理解:

UDP 无连接,发就完了 不保证送达,丢了不管 没有拥塞控制 头部只有8字节 支持广播和组播 实时性好,延迟低 适合:视频流、游戏、DNS TCP 面向连接,先握手 保证送达,丢包重传 有拥塞控制,会限速 头部至少20字节 只支持单播 实时性差,延迟高 适合:网页、文件传输、邮件

在FPGA项目里怎么选?我个人的经验是:

  • 视频图像传输:用UDP。丢几帧画面无所谓,但延迟必须低。我做过一个摄像头采集项目,用UDP传输1080p视频,延迟控制在1ms以内。
  • 控制指令下发:用UDP+应用层确认。纯TCP在FPGA里实现太复杂,资源消耗大。我一般自己写一个简单的确认机制,比TCP轻量得多。
  • 数据采集上传:看情况。如果数据量小且重要,用TCP;如果数据量大且允许丢点,用UDP。

一句话总结:UDP是“快但不可靠”,TCP是“可靠但慢”。在FPGA里,UDP实现简单、资源占用少,是绝大多数场景的首选。TCP虽然可靠,但状态机复杂,资源消耗大,除非必须保证数据完整,否则我一般不用。

好了,UDP协议的核心内容就这些。下一节我们会动手在FPGA里实现UDP的发送和接收,到时候你会更深刻地理解这些概念在实际工程中是怎么落地的。


专注资料整理