深入浅出 UDP 通信:单播、广播、组播、多端口推送与结构体序列化

适用对象:网络通信、嵌入式开发、GNSS 终端和定位算法初学者
本文主线:UDP 基础 → IP 与端口 → 单播/广播/组播/多端口推送 → 发送接收 → 网络可达性 → 序列化 → GNSS 应用


1. UDP 是什么

UDP 的英文全称是:

User Datagram Protocol

中文通常译为:

用户数据报协议

正确缩写是 UDP,不是 UPD。

UDP 位于传输层,通常承载在 IP 协议之上:

应用层数据
→ UDP
→ IP
→ 以太网、Wi-Fi、4G、5G或其他链路

UDP 的主要特点是:

  • 面向数据报;
  • 无连接;
  • 保留消息边界;
  • 不保证送达;
  • 不保证顺序;
  • 不自动重传;
  • 不自动去重;
  • 不提供 TCP 式流量控制和拥塞控制。

“UDP 不可靠”不是说 UDP 一定丢包,而是:

UDP 协议本身不承诺数据一定送达,也不负责修复丢包、乱序和重复。


2. UDP 通信先要理解 IP 和端口

一条 UDP 数据报通常由以下四个量标识:

源IP
源端口
目标IP
目标端口

它们合起来称为一个通信四元组:

(IPsrc,Portsrc,IPdst,Portdst)\left( IP_{\mathrm{src}}, Port_{\mathrm{src}}, IP_{\mathrm{dst}}, Port_{\mathrm{dst}} \right)(IPsrc,Portsrc,IPdst,Portdst)

其中:

  • IPsrcIP_{\mathrm{src}}IPsrc:发送端 IP 地址;
  • PortsrcPort_{\mathrm{src}}Portsrc:发送端 UDP 端口;
  • IPdstIP_{\mathrm{dst}}IPdst:目标 IP 地址;
  • PortdstPort_{\mathrm{dst}}Portdst:目标 UDP 端口。

可以简单理解为:

IP地址
→ 找到哪台主机或哪一组主机

端口
→ 找到主机上的哪个应用程序

例如:

192.168.1.100:9000

表示目标主机地址为 192.168.1.100,目标 UDP 端口为 9000

2.1 UDP 首部为什么需要端口

UDP 首部固定为 8 字节:

字段长度作用
源端口2 字节标识发送端应用;协议格式允许为 0
目标端口2 字节标识接收端应用
UDP 长度2 字节UDP 首部与负载的总长度
校验和2 字节检测传输差错

普通 UDP 数据报长度为:

LUDP=8+LpayloadL_{\mathrm{UDP}} = 8 + L_{\mathrm{payload}}LUDP=8+Lpayload

其中:

  • LUDPL_{\mathrm{UDP}}LUDP:UDP 首部和负载的总长度;
  • LpayloadL_{\mathrm{payload}}Lpayload:应用层负载长度;
  • 8:UDP 首部长度,单位为字节。

UDP 校验和是差错检测机制,不提供身份认证,也不能代替 HMAC、AEAD 等密码学保护。


3. UDP 端口的有效范围

UDP 端口字段为 16 位无符号整数,因此数值范围为:

0≤Port≤655350 \le Port \le 655350Port65535

其中:

  • PortPortPort:UDP 端口号;
  • 0:保留端口;
  • 65535:最大端口号。

IANA 将端口分为三段:

范围名称含义
0~1023System Ports系统端口或知名端口
1024~49151User Ports用户端口或注册端口
49152~65535Dynamic/Private Ports动态端口或私有端口

3.1 端口 0 怎样理解

端口 0 被保留,通常不作为正常服务的目标端口。

需要区分两种情况:

  1. UDP 首部中的源端口字段为 0:协议格式表示发送方没有提供可用源端口;
  2. 套接字执行 bind() 时请求端口 0:操作系统会选择一个可用的非零本地端口。

因此:

bind(fd, port = 0)

通常表示:

请操作系统自动分配一个临时本地端口。

它并不表示该套接字最终长期使用端口 0。

3.2 自定义 UDP 服务用哪个端口

长期部署的自定义服务通常从:

1024~49151

中选择未占用端口。

短期测试、客户端临时端口或完全受控的内部系统,也可能使用动态/私有端口。公开部署前应检查 IANA 注册和本机已有服务,避免端口冲突。

例如:

9000
10001
20000

在 Linux、Unix 等系统中,绑定 0~1023 端口通常需要管理员权限或相应 capability。

端口是否可用不能只看数值范围,还要确认:

  • 没有被其他程序占用;
  • 没有与已部署服务冲突;
  • 防火墙允许该端口;
  • 双方配置一致。

3.3 临时源端口不一定覆盖整个动态范围

发送方不显式 bind() 时,操作系统会自动分配临时源端口。

实际临时端口范围由操作系统配置决定,不一定恰好等于:

49152~65535

3.4 TCP 和 UDP 的端口空间相互独立

同一台主机可以同时存在:

TCP 9000
UDP 9000

因为 TCP 和 UDP 是不同传输协议。

但是两个普通 UDP 套接字通常不能随意同时独占绑定完全相同的本地 IP 和端口,除非使用特定的地址复用选项,并满足操作系统规则。


4. IPv4 地址不是都能作为普通目标地址

IPv4 地址共有 32 位,理论数值范围为:

0.0.0.0 ~ 255.255.255.255

但这个范围中包含:

  • 普通单播地址;
  • 私有地址;
  • 环回地址;
  • 链路本地地址;
  • 组播地址;
  • 广播地址;
  • 文档示例地址;
  • 其他特殊用途地址。

因此,不能简单地把所有 IPv4 地址都当作普通主机地址。

4.1 常见 IPv4 特殊地址

地址范围用途能否作为普通公网目标
0.0.0.0未指定地址;绑定时常表示所有本地接口
10.0.0.0/8私有地址不能直接在公网路由
172.16.0.0/12私有地址不能直接在公网路由
192.168.0.0/16私有地址不能直接在公网路由
127.0.0.0/8本机环回地址只能用于本机
169.254.0.0/16IPv4 链路本地地址通常不跨路由器
224.0.0.0/4IPv4 组播地址不是普通单播地址
255.255.255.255受限广播地址只用于本地链路广播

公网单播地址不是一个可以简单写成“某个起点到某个终点”的连续区间。实际使用时应确认地址:

  • 已经分配给目标设备或网络;
  • 不属于 IANA 特殊用途地址;
  • 可以通过当前网络路由到达。

5. 一个子网中的有效主机地址

以 IPv4 子网为例:

网络:192.168.1.0/24
掩码:255.255.255.0

典型地址划分为:

地址含义
192.168.1.0子网地址
192.168.1.1~192.168.1.254普通主机单播地址
192.168.1.255该子网的定向广播地址

在普通广播型子网中,主机部分全 0 是网络地址,主机部分全 1 是广播地址。

但这不是没有例外的绝对规则。例如:

  • /31 可用于点到点链路;
  • /32 表示单个主机路由。

对于初学者,在普通局域网中可以先按“网络地址和广播地址不能分配给主机”理解。


第一部分:UDP 的三种通信模式

6. 单播是什么

单播(Unicast)是一对一通信:

一个发送方
→ 一个明确的目标IP和端口
→ 一个接收方套接字

例如:

发送方:192.168.1.10:52000
接收方:192.168.1.100:9000

发送方调用:

sendto(
    fd,
    data,
    length,
    0,
    192.168.1.100:9000
);

6.1 单播目标 IP 的有效范围

单播没有一个简单、固定的“有效起止范围”。

目标地址应满足:

  • 是分配给某个接口或主机的单播地址;
  • 不属于组播地址;
  • 不属于广播地址;
  • 不是未指定地址;
  • 在当前网络中可达。

常见可用于 UDP 单播的 IPv4 地址包括:

  • 局域网中的私有地址;
  • 公网中已分配的全球可路由地址;
  • 本机测试使用的 127.0.0.1
  • 同一链路上的 169.254.x.x,但通常不能跨路由器。

6.2 单播双方怎样准备

发送方通常需要知道:

接收方IP
+
接收方端口

接收方通常需要:

创建UDP套接字
+
bind绑定本地端口
+
配置防火墙
+
调用recvfrom等待数据

6.3 单播适合什么场景

  • GNSS 终端向服务器上报位置;
  • 服务器向指定终端发送命令;
  • 一对一状态查询;
  • DNS、NTP 等请求—响应;
  • 两台设备之间的点对点数据传输。

7. 广播是什么

广播(Broadcast)表示:

一个发送方
→ 同一IPv4广播域内的所有接收方

广播只存在于 IPv4,IPv6 不提供广播地址。

UDP 可以承载 IPv4 广播数据报。

7.1 受限广播地址

固定地址为:

255.255.255.255

它表示:

向当前本地链路发送广播数据报。链路上的主机都可能收到,但只有符合端口、绑定和防火墙条件的套接字才会交付给应用程序。

这种广播不会被路由器转发。

7.2 子网定向广播地址

子网定向广播地址由以下规则得到:

网络前缀保持不变
+
主机位全部置1

例如:

192.168.1.0/24
→ 广播地址 192.168.1.255

再例如:

10.20.0.0/16
→ 广播地址 10.20.255.255

因此,广播地址没有一个统一固定的数值范围,而是取决于:

  • 子网地址;
  • 子网前缀长度或子网掩码。

计算公式为:

IPbroadcast=IPnetworkORNOT⁡(Mask)IP_{\mathrm{broadcast}} = IP_{\mathrm{network}} \mathbin{\mathrm{OR}} \operatorname{NOT}(Mask)IPbroadcast=IPnetworkORNOT(Mask)

其中:

  • IPbroadcastIP_{\mathrm{broadcast}}IPbroadcast:子网广播地址;
  • IPnetworkIP_{\mathrm{network}}IPnetwork:子网网络地址;
  • MaskMaskMask:子网掩码;
  • OR:按位或;
  • NOT:按位取反,计算时只保留 IPv4 的 32 位。

7.3 广播发送方和接收方怎样准备

发送方需要:

  1. 创建 IPv4 UDP 套接字;
  2. 启用 SO_BROADCAST
  3. 确定广播地址;
  4. 指定接收端共同监听的端口;
  5. 调用 sendto()

接收方需要:

  1. 创建 UDP 套接字;
  2. 绑定约定端口;
  3. 通常绑定 0.0.0.0
  4. 配置防火墙;
  5. 调用 recvfrom()

典型发送端关键调用为:

int enable = 1;

setsockopt(
    fd,
    SOL_SOCKET,
    SO_BROADCAST,
    &enable,
    sizeof(enable)
);

/* destination可设置为255.255.255.255,
 * 或当前子网的定向广播地址。
 */
sendto(fd, data, length, 0, ...);

接收端不需要设置 SO_BROADCAST,通常只需绑定双方约定的 UDP 端口。

若同一主机上的多个程序都希望监听相同广播端口,可能需要在 bind() 前设置 SO_REUSEADDR。是否能让多个程序都收到同一广播数据报,还与操作系统的套接字复用规则有关。

7.4 广播的限制

  • 只能用于 IPv4;
  • 通常只在同一广播域或子网中有效;
  • 普通路由器默认不转发定向广播;
  • 所有监听设备都会收到,容易产生无关流量;
  • 不适合跨公网通信;
  • 大量广播可能形成广播风暴。

7.5 广播适合什么场景

  • 局域网设备发现;
  • 终端首次寻找服务器;
  • 小规模局域网状态通知;
  • DHCP 等本地网络协议。

8. 组播是什么

组播,也称多播(Multicast),表示:

一个发送方
→ 一个组播组地址
→ 所有已加入该组的接收方

它介于单播和广播之间:

单播
→ 只发给一个目标

广播
→ 发给广播域中的所有设备

组播
→ 只发给主动加入该组的设备

8.1 IPv4 组播地址范围

IPv4 组播地址固定为:

224.0.0.0 ~ 239.255.255.255

即:

224.0.0.0/4

常见子范围:

地址范围含义
224.0.0.0/24本地网络控制组播,通常不被路由器转发
232.0.0.0/8源特定组播 SSM
239.0.0.0/8管理域组播,类似组播中的私有地址

自定义局域网程序常选择:

239.x.x.x

例如:

239.10.20.30:9000

但仍应避免与网络中已有组播业务冲突。

8.2 IPv6 组播地址范围

IPv6 组播地址固定为:

ff00::/8

也就是说,IPv6 组播地址都以 ff 开头。

IPv6 组播地址第二个字节的低 4 位表示作用域。常见作用域值包括:

作用域值含义
1接口本地
2链路本地
5站点范围
8组织范围
e全球范围

例如,ff02::1 是链路本地“所有节点”组播地址。自定义业务不能只修改作用域位后随意使用同一个组标识,而应选择已经分配或合理规划的组播地址。

IPv6 没有广播,许多原本需要广播的功能由组播实现。

8.3 组播发送方怎样准备

发送方通常需要:

  1. 创建 UDP 套接字;
  2. 设置目标组播 IP 和端口;
  3. 选择发送网卡;
  4. 设置 TTL 或 Hop Limit;
  5. 调用 sendto()

发送方通常不需要加入组播组,除非它自己也需要接收该组数据。

8.4 组播接收方怎样准备

接收方通常需要:

  1. 创建 UDP 套接字;
  2. 绑定约定端口;
  3. 指定本地网卡;
  4. 加入组播组;
  5. 调用 recvfrom()

IPv4 常使用:

IP_ADD_MEMBERSHIP

IPv6 常使用:

IPV6_JOIN_GROUP

仅仅绑定端口并不一定足够,接收方还必须加入目标组播组。

组播接收实际上需要同时满足三项:

组播组地址匹配
+
接收网卡匹配
+
UDP目标端口匹配

IP_ADD_MEMBERSHIPIPV6_JOIN_GROUP 负责“组地址与网卡”,bind() 负责“UDP 端口”。组播组成员关系本身不包含 UDP 端口。

IPv4 接收端的关键操作示意:

struct ip_mreq membership;

membership.imr_multiaddr.s_addr =
    inet_addr("239.10.20.30");

membership.imr_interface.s_addr =
    htonl(INADDR_ANY);

setsockopt(
    fd,
    IPPROTO_IP,
    IP_ADD_MEMBERSHIP,
    &membership,
    sizeof(membership)
);

发送端可设置组播 TTL:

unsigned char ttl = 1;

setsockopt(
    fd,
    IPPROTO_IP,
    IP_MULTICAST_TTL,
    &ttl,
    sizeof(ttl)
);

其中:

  • TTL 为 1 时,组播通常只在当前子网传播;
  • 多网卡设备应明确选择组播发送和接收接口,不宜依赖 INADDR_ANY 的自动选择;
  • 同一主机运行多个组播接收程序时,可能需要 SO_REUSEADDR,其具体行为与操作系统有关;
  • 对同一端口的普通单播,地址复用往往是负载分配,而不是保证每个套接字都收到一份副本。

8.5 组播能否跨网段

可以,但需要网络设备支持:

  • 组播路由;
  • IGMP 或 MLD;
  • 交换机组播侦听;
  • 合理的 TTL 或 Hop Limit;
  • 防火墙放行。

普通家庭或企业网络不一定已经配置跨网段组播。

8.6 组播适合什么场景

  • 一份 GNSS 改正数据同时发给多个终端;
  • 局域网行情、遥测和状态播发;
  • 多终端同步数据;
  • 音视频一对多分发;
  • 不希望所有设备都接收的群组通信。

9. 多端口播发与多端口推送

“多端口播发”和“多端口推送”不是 UDP 标准中定义的第四种通信模式,而是工程中的应用层说法。

它通常表示:

一个发送程序把同一份数据或不同类型的数据,发送到多个目标端点。

一个 UDP 目标端点由以下两部分组成:

Endpointi=(IPi,Porti)Endpoint_i = \left( IP_i, Port_i \right)Endpointi=(IPi,Porti)

其中:

  • EndpointiEndpoint_iEndpointi:第 iii 个目标端点;
  • IPiIP_iIPi:第 iii 个目标 IP;
  • PortiPort_iPorti:第 iii 个目标 UDP 端口。

9.1 一条 UDP 数据报能否同时指定多个端口

不能。

一次 sendto() 只能指定一个目标套接字地址,即:

一个目标IP
+
一个目标端口

因此,一条 UDP 数据报不能同时写成:

发送到9001、9002、9003三个端口

若要向多个端口发送,应用程序必须:

  • 多次调用 sendto()
  • 或使用 sendmmsg() 等批量接口一次提交多条数据报;
  • 或改用广播、组播等一对多方式。

批量接口只减少系统调用次数,并不会把“一条 UDP 数据报”变成“多个目标”。每条数据报仍只有一个目标端点。

9.2 多端口推送的三种常见形式

形式一:同一主机、多个端口
192.168.1.100:9001
192.168.1.100:9002
192.168.1.100:9003

适合将同一份 GNSS 结果发送给同一设备上的多个独立程序,例如:

  • 9001:实时显示;
  • 9002:数据记录;
  • 9003:状态监控。

每个端口通常对应一个已经绑定该端口的接收套接字。

形式二:多个主机、相同端口
192.168.1.101:9000
192.168.1.102:9000
192.168.1.103:9000

适合多个终端运行同一种接收服务。

这属于:

重复单播
或
单播扇出
形式三:多个主机、不同端口
192.168.1.101:9001
192.168.1.102:10001
192.168.1.103:20001

适合各终端端口配置不同的场景。

发送方需要维护完整的目标端点列表,而不能只保存 IP 列表。

9.3 多端口推送是否支持一对多

支持。

其本质是:

一个发送程序
→ 多个(IP, Port)目标

但应区分两种“一对多”:

应用层重复单播

发送方针对每个目标调用一次 sendto()

Nsend=NendpointN_{\mathrm{send}} = N_{\mathrm{endpoint}}Nsend=Nendpoint

其中:

  • NsendN_{\mathrm{send}}Nsend:同一轮需要执行的发送次数;
  • NendpointN_{\mathrm{endpoint}}Nendpoint:目标端点数量。

若有 100 个目标,发送方通常需要发送 100 份数据。

IP 组播或 IPv4 广播

发送方只向一个广播地址或组播地址发送,网络负责复制数据。

因此:

  • 目标较少、地址各不相同:重复单播较简单;
  • 目标较多、数据完全相同、网络支持组播:组播通常更节省发送端带宽;
  • 局域网设备发现:广播或组播更合适。

9.4 一个套接字能否发送到多个端口

可以。

同一个未连接 UDP 套接字可以多次调用 sendto(),每次使用不同的目标地址。

例如:

struct Destination {
    const char *ip;
    uint16_t port;
};

struct Destination destinations[] = {
    {"192.168.1.100", 9001},
    {"192.168.1.100", 9002},
    {"192.168.1.101", 9000}
};

for (size_t i = 0;
     i < sizeof(destinations) / sizeof(destinations[0]);
     ++i) {

    struct sockaddr_in target;
    memset(&target, 0, sizeof(target));

    target.sin_family = AF_INET;
    target.sin_port = htons(destinations[i].port);

    if (inet_pton(
            AF_INET,
            destinations[i].ip,
            &target.sin_addr) != 1) {
        continue;
    }

    sendto(
        fd,
        buffer,
        length,
        0,
        (const struct sockaddr *)&target,
        sizeof(target)
    );
}

使用同一个套接字时,所有数据报通常共享同一个本地源端口。

在多网卡主机上,若套接字只绑定通配地址,不同目标可能根据路由选择不同的源 IP,但源端口通常保持不变。

如果该套接字没有显式 bind(),操作系统会在首次发送时自动分配本地端口。

若 UDP 套接字已经通过 connect() 固定了对端,就不应再把它当作面向任意目标的多端口发送套接字;跨平台实现中应使用未连接套接字执行多目标 sendto()

9.5 接收多个本地端口怎样实现

一个普通 UDP 套接字通常绑定一个本地端口。

若一个程序需要同时监听:

9001
9002
9003

通常需要:

  • 创建三个 UDP 套接字;
  • 分别绑定三个端口;
  • 使用 select()poll()epoll() 统一等待事件;
  • 或为不同端口使用不同接收线程。

示意:

socket_1 → bind 9001
socket_2 → bind 9002
socket_3 → bind 9003

不建议为了“少创建套接字”而使用原始套接字解析所有 UDP 流量,这会增加权限、安全和协议处理复杂度。

还要区分“多个端口”和“多个套接字共用一个端口”:

情况常见结果
一个程序监听多个不同端口通常需要多个套接字
多个普通单播套接字复用同一端口可能由内核选其中一个套接字接收
多个广播或组播套接字复用同一端口可能每个套接字都收到副本,但依赖操作系统和绑定方式

因此,不能仅凭设置 SO_REUSEADDRSO_REUSEPORT,就假设同一单播数据报会复制给所有监听程序。

9.6 多端口推送与广播、组播的区别

方式发送动作接收者范围发送端数据副本
多端口重复单播对每个端点分别发送明确列出的目标约等于目标数
IPv4 广播向一个广播地址发送广播域内监听端口的设备1 份
IP 组播向一个组播地址发送已加入组的设备1 份
同一主机多端口向同一 IP 的多个端口分别发送同一主机上的多个服务等于端口数

“多端口”强调的是目标端口列表;“组播”强调的是组地址和组成员关系,二者不是同一个概念。

9.7 多端口推送的优点

  • 不要求网络支持组播;
  • 每个接收端可以使用不同 IP 和端口;
  • 可以对不同目标发送不同消息;
  • 可以单独记录每个目标的发送状态;
  • 容易对单个目标启用、暂停或调整频率。

9.8 多端口推送的局限

  • 目标越多,发送次数和出口流量越大;
  • 任一目标不可达,不代表其他目标也失败;
  • UDP 不会自动确认每个目标是否收到;
  • 目标列表需要维护和更新;
  • 跨 NAT 推送时,每个终端都必须具有可达映射;
  • 同一数据连续发送多个目标时,需要控制速率,避免瞬时突发。

9.9 GNSS 中的多端口推送

典型场景一:

PPP定位引擎
├─ 127.0.0.1:9001 → 实时绘图
├─ 127.0.0.1:9002 → 数据落盘
└─ 192.168.1.50:9003 → 监控终端

典型场景二:

改正数服务器
├─ 终端A:192.168.1.101:10001
├─ 终端B:192.168.1.102:10002
└─ 终端C:192.168.1.103:10003

若所有终端接收完全相同的 RTCM 或 SSR 数据,并且网络已经支持组播,优先评估组播。

若目标数量较少、地址异构或需要逐终端控制,多端口重复单播更容易实施。

9.10 多端口推送流程图

说明

GNSS数据源

序列化一次

目标端点列表

IP1:Port1

IP2:Port2

IP3:Port3

接收程序1

接收程序2

接收程序3

每个sendto只指定一个目标端点


10. 单播、广播、组播和多端口推送对比

对比项单播IPv4 广播IP 组播多端口重复单播
通信关系一对一一对广播域内全部主机一对已加入组的接收者一个程序对多个端点逐一发送
IPv4支持支持支持支持
IPv6支持不支持支持支持
目标地址普通单播地址受限或子网广播地址IPv4 224.0.0.0/4;IPv6 ff00::/8多个普通 (IP, Port)
一次数据报的目标数11 个广播地址1 个组播地址每条数据报仍为 1
是否通常跨路由器可以默认不可以取决于组播路由配置可以
接收方特殊准备绑定端口绑定端口绑定端口并加入组播组各目标分别绑定对应端口
发送端出口副本111约等于目标端点数
典型场景终端上报服务器局域网发现一份数据分发多个终端少量异构目标逐个推送

多端口推送不是新的 IP 寻址模式,而是使用单播对多个目标端点循环发送的应用层策略。


11. 三种通信模式流程图

单播

IPv4广播

组播

多端口推送

UDP发送方

选择通信模式

填写一个目标单播IP和端口

仅目标主机接收

填写广播地址和端口

广播域内监听该端口的主机接收

填写组播组地址和端口

网络向已加入组的接收方复制数据

遍历多个IP和端口

每个目标分别发送一条数据报

单播接收方

bind本地端口

广播接收方

bind共同端口

组播接收方

bind端口并加入组播组


第二部分:IPv6 地址补充

12. IPv6 常见地址范围

IPv6 地址为 128 位。

常见地址类型如下:

地址范围用途
::未指定地址
::1本机环回地址
2000::/3全球单播地址
fc00::/7唯一本地地址 ULA
fe80::/10链路本地单播地址
ff00::/8组播地址
2001:db8::/32文档示例地址,不用于实际生产网络

工程中常见的本地 IPv6 地址包括:

  • fe80::/10:只在当前链路内使用;
  • fd00::/8:常用于本地生成的 ULA 地址;
  • 2000::/3:全球单播地址空间。

当设备具有多个网卡时,仅写一个链路本地地址通常不足以确定链路,还需要指定接口或作用域 ID,例如:

fe80::1234%eth0

在套接字编程中通常通过 sin6_scope_id 指定接口。

IPv6 没有广播地址。


第三部分:UDP 发送与接收

13. UDP 的发送者、接收者、客户端和服务器

UDP 协议本身不强制规定客户端和服务器。

应用程序通常约定:

服务器
→ 绑定稳定端口,等待数据

客户端
→ 知道服务器地址,主动发送第一条数据

对于某一条数据报:

谁发送
→ 谁是发送者

谁接收
→ 谁是接收者

同一程序可以同时发送和接收。

例如:

GNSS终端 → 服务器:位置与观测值
服务器   → GNSS终端:配置与控制命令

14. 接收方需要做什么准备

典型接收流程:

socket()
→ bind()
→ recvfrom()
→ 校验
→ 反序列化

接收方通常需要:

  1. 创建 SOCK_DGRAM 套接字;
  2. 绑定本地 IP 和端口;
  3. 配置防火墙;
  4. 调用 recvfrom()recvmsg()
  5. 检查来源、长度和截断状态;
  6. 解析应用层消息。

绑定:

0.0.0.0:9000

表示接收所有本地 IPv4 网卡上到达端口 9000 的数据。

绑定:

[::]:9000

表示监听所有本地 IPv6 地址上到达该端口的数据。

该套接字是否同时接收 IPv4 映射地址,取决于操作系统和 IPV6_V6ONLY 设置,不能假设所有平台行为一致。

接收方通常不需要提前知道发送方地址,recvfrom() 会返回源 IP 和源端口。


15. 发送方需要做什么准备

典型发送流程:

socket()
→ 确定目标地址和端口
→ 序列化
→ sendto()

单播发送方通常需要知道:

  • 接收方 IP 或域名;
  • 接收方端口;
  • 应用层协议格式。

发送方不显式 bind() 时,操作系统通常会自动选择:

  • 本地 IP;
  • 临时源端口。

需要固定源端口、固定网卡或稳定接收回复时,可以主动 bind()


16. UDP 的 connect() 怎样理解

UDP 套接字也可以调用 connect(),但不会进行 TCP 三次握手。

调用后,操作系统通常会:

  • 保存默认目标地址;
  • 允许使用 send()recv()
  • 过滤非指定对端的数据报;
  • 更方便地报告部分网络错误。

它仍然不会自动获得:

  • 可靠送达;
  • 重传;
  • 有序交付;
  • 流量控制。

17. 最小发送和接收接口

17.1 接收

ssize_t received = recvfrom(
    fd,
    buffer,
    buffer_size,
    0,
    (struct sockaddr *)&peer_addr,
    &peer_len
);

需要检查:

  • 返回值是否小于 0;
  • 返回长度是否符合协议;
  • 接收缓冲区是否足够;
  • 数据报是否被截断;
  • 消息版本和负载长度是否合法。

UDP 允许零长度数据报,因此:

received == 0

表示收到一条空数据报,不表示对端关闭连接。

17.2 发送

ssize_t sent = sendto(
    fd,
    buffer,
    length,
    0,
    (const struct sockaddr *)&destination,
    sizeof(destination)
);

对 UDP 数据报套接字,一条消息按整体提交。

sendto() 成功通常只表示数据被本机协议栈接受,不代表:

  • 接收方已经收到;
  • 接收方业务已经处理;
  • 中间网络没有丢包。

第四部分:网络环境与可达性

18. 双方必须位于同一局域网吗

不必须。

UDP 可用于:

  • 同一主机;
  • 同一局域网;
  • 不同局域网;
  • 企业专网;
  • 互联网;
  • 4G 或 5G;
  • VPN;
  • 卫星通信链路。

关键条件是:

IP可达
+
目标端口可达
+
路由、NAT和防火墙允许

19. 同一局域网通信条件

通常需要:

  • IP 地址与子网配置正确;
  • 设备之间能够互通;
  • 接收程序已经绑定端口;
  • 防火墙允许该 UDP 端口;
  • Wi-Fi 没有启用客户端隔离;
  • 不存在重复 IP 地址。

测试步骤:

  1. 检查双方 IP、掩码和网关;
  2. 使用 ping 初步检查 IP 连通性;
  3. 使用 Wireshark 或 tcpdump 抓包;
  4. 检查程序绑定的 IP 和端口;
  5. 检查防火墙。

ping 与 UDP 使用不同协议:

  • ping 成功不代表 UDP 端口一定可用;
  • ping 失败也不一定代表 UDP 不可达。

20. 跨互联网、NAT 与防火墙

接收方具有公网可达地址时,发送方可以向:

公网IP:UDP端口

发送数据。

接收方位于 NAT 后时,可能需要:

  • 端口映射;
  • VPN;
  • UDP 打洞;
  • 公网中继;
  • 终端主动注册和心跳。

UDP 打洞并非总能成功。对称 NAT 或严格防火墙下,通常需要中继服务器。

移动网络终端常处于运营商级 NAT 后,常见模式是:

终端主动向公网服务器发送
→ 服务器记录外部IP和端口
→ 周期性心跳维持映射

第五部分:结构体、序列化与数据报长度

21. UDP 能否发送结构体

可以发送结构体所表达的信息,但不应直接把结构体内存当作通用网络协议。

内存中的结构体
≠
网络字节格式

直接发送:

sendto(fd, &position, sizeof(position), 0, ...);

可能受到:

  • 结构体填充;
  • 内存对齐;
  • CPU 字节序;
  • 类型宽度;
  • 浮点表示;
  • 编译器和 ABI;
  • 软件版本;
  • 指针成员;

影响。


22. 什么是内存对齐

例如:

struct Example {
    uint8_t  type;
    uint32_t sequence;
    uint16_t length;
};

成员长度相加为:

1+4+2=7 bytes1+4+2=7\ \text{bytes}1+4+2=7 bytes

sizeof(struct Example) 可能为 12 字节。

一种可能布局为:

type          1字节
padding       3字节
sequence      4字节
length        2字节
tail padding  2字节

因此:

sizeof⁡(struct)≠∑成员长度\operatorname{sizeof}(\text{struct}) \ne \sum \text{成员长度}sizeof(struct)=成员长度

不应依赖结构体实际内存布局定义网络协议。


23. #pragma pack(1) 不能解决什么

它只能减少填充,不能解决:

  • 字节序;
  • 类型宽度;
  • 浮点格式;
  • enumbool 表示;
  • 版本兼容;
  • 指针成员;
  • 非对齐访问;
  • 编译器扩展差异。

因此,packed 结构体不能替代序列化。


24. 序列化和反序列化

序列化:

结构体或对象
→ 按协议写入字节数组

反序列化:

字节数组
→ 检查长度和版本
→ 读取字段
→ 生成结构体或对象

双方必须约定:

  • 字段顺序;
  • 字段长度;
  • 字节序;
  • 有符号整数表示;
  • 浮点格式;
  • 字符串编码;
  • 消息版本;
  • 最大报文长度。

常见方案:

方案特点
手工定长二进制开销小,适合嵌入式和 GNSS 高频状态
TLV易扩展,可跳过未知字段
Protocol Buffers跨语言,版本兼容较好
CBOR紧凑且具有一定自描述能力
JSON易读,但体积较大

25. 网络字节序

网络协议通常规定多字节整数采用大端字节序。

函数作用
htons()16 位主机字节序转网络字节序
htonl()32 位主机字节序转网络字节序
ntohs()16 位网络字节序转主机字节序
ntohl()32 位网络字节序转主机字节序

推荐逐字段编码,不要通过未对齐指针直接读取网络缓冲区。


26. 推荐的应用层消息格式

字段作用
魔数判断是否属于本协议
版本处理协议升级
消息类型区分位置、状态和观测值
序列号检测丢包、重复和乱序
时间戳判断数据时效
负载长度防止越界解析
负载业务数据
认证标签身份认证和完整性保护

序列号只能帮助发现丢包和乱序,不能阻止丢包。


27. 一个 UDP 数据报可以放多少数据

普通 IPv4 UDP 理论最大有效负载为:

Lpayload,maxIPv4=65535−20−8=65507 bytesL_{\mathrm{payload,max}}^{\mathrm{IPv4}} = 65535-20-8 = 65507\ \text{bytes}Lpayload,maxIPv4=65535208=65507 bytes

其中:

  • 65535:IPv4 数据包最大总长度;
  • 20:IPv4 基本首部长度;
  • 8:UDP 首部长度。

普通 IPv6 UDP 理论最大有效负载为:

Lpayload,maxIPv6=65535−8=65527 bytesL_{\mathrm{payload,max}}^{\mathrm{IPv6}} = 65535-8 = 65527\ \text{bytes}Lpayload,maxIPv6=655358=65527 bytes

理论最大值不等于推荐长度。

以 MTU 为 1500 字节为例:

LpayloadIPv4≤1500−20−8=1472 bytesL_{\mathrm{payload}}^{\mathrm{IPv4}} \le 1500-20-8 = 1472\ \text{bytes}LpayloadIPv41500208=1472 bytes

LpayloadIPv6≤1500−40−8=1452 bytesL_{\mathrm{payload}}^{\mathrm{IPv6}} \le 1500-40-8 = 1452\ \text{bytes}LpayloadIPv61500408=1452 bytes

工程上应尽量避免 IP 分片:

  • IPv4 数据包可能由源主机或中间路由器分片,也可能因禁止分片而发送失败;
  • IPv6 路由器不会替终端分片,源端需要根据路径 MTU 调整数据包;
  • 任一分片丢失,整条 UDP 数据报通常都无法交付给应用程序。

28. UDP 会不会粘包

UDP 保留数据报边界。

发送方两次调用:

sendto(A)
sendto(B)

接收方不会把它们自动合并成一条 UDP 数据报,因此 UDP 没有 TCP 意义上的粘包。

但如果接收缓冲区太小,数据报可能被截断,剩余部分不会在下一次 recvfrom() 中继续返回。

程序应预先定义最大报文长度。需要可靠判断是否截断时,可使用 recvmsg() 并检查 MSG_TRUNC;不同平台对 recvfrom() 返回长度的细节可能不同。


第六部分:UDP 与 TCP、可靠性和安全

29. UDP 与 TCP 的区别

对比项UDPTCP
服务形式数据报字节流
建立通信不握手需要建立连接
消息边界保留不保留
可靠送达不保证向对端 TCP 协议栈提供可靠、有序字节流
顺序不保证保证字节顺序
重传应用自行设计TCP 自动处理
流量控制无 TCP 式机制
拥塞控制应用需要负责
广播/组播可结合 IP 广播和组播不支持原生广播和组播
典型接口sendto/recvfromconnect/listen/accept/send/recv

TCP 确认字节到达对端 TCP 协议栈,不代表对端业务已经执行成功。业务级成功仍需应用层应答。


30. UDP 的优势与适用场景

UDP 的优势:

  • 首部只有 8 字节;
  • 不需要连接握手;
  • 保留消息边界;
  • 可以丢弃过期数据;
  • 应用可自定义确认和重传;
  • 可结合广播和组播。

UDP 不保证一定比 TCP 低时延,实际时延仍受网络和应用影响。

适合 UDP:

实时性高于完整性
旧数据没有重传价值
消息具有天然边界
需要广播或组播
应用愿意自行管理可靠性

适合 TCP:

每个字节必须可靠到达
数据必须严格有序
文件、日志和配置不能缺失

31. 应用层怎样增加可靠性

可增加:

  • 序列号;
  • 时间戳;
  • 超时;
  • 心跳;
  • ACK;
  • 重传;
  • 速率控制;
  • 前向纠错。

关键控制命令还应增加:

  • 唯一事务号;
  • 幂等处理;
  • 重复命令缓存;
  • 明确的成功或失败状态。

简单增加 ACK 并不等于“恰好执行一次”。


32. UDP 安全性

UDP 本身不提供:

  • 身份认证;
  • 加密;
  • 防重放;
  • 防伪造;
  • 访问控制。

可采用:

  • DTLS;
  • QUIC;
  • VPN;
  • HMAC;
  • AEAD;
  • 时间戳和随机数;
  • 防重放窗口;
  • 白名单和速率限制。

源 IP 和端口不能单独作为可靠身份凭证。


第七部分:GNSS 工程应用

33. GNSS 中怎样选择通信模式

33.1 单播

适合:

GNSS终端 → 定位服务器
服务器 → 指定终端

例如:

  • 位置上报;
  • 原始观测上传;
  • 单终端配置;
  • 点对点状态查询。

33.2 广播

适合小型 IPv4 局域网中的:

  • 设备发现;
  • 查找服务器;
  • 临时状态通知。

不适合跨公网和大规模网络。

33.3 组播

适合:

一个服务端
→ 多个GNSS终端

例如:

  • 一份 RTCM 数据同时发送多个接收机;
  • 一份 SSR 改正数据发送多个终端;
  • 多终端同步状态;
  • 局域网实时位置分发。

组播比广播更有选择性,但网络设备必须支持组播。

33.4 多端口重复单播

适合:

  • 同一定位结果同时发给绘图、记录和监控程序;
  • 目标终端数量较少;
  • 各终端使用不同 IP 或端口;
  • 需要逐目标启停和统计;
  • 网络不支持组播。

目标数量较多且数据完全相同时,应比较重复单播和组播的出口带宽、网络配置及维护成本。


34. GNSS 数据的协议设计建议

位置和状态数据建议包含:

  • GNSS 时间;
  • 序列号;
  • 定位模式;
  • 坐标;
  • 卫星数;
  • DOP;
  • 数据长度;
  • 协议版本。

原始观测、RTCM 或 SSR 数据还应考虑:

  • 完整帧边界;
  • 分块编号;
  • 丢包统计;
  • 超时重组;
  • 数据龄期;
  • 安全认证。

RTCM 3 帧自带 CRC-24Q,只能检测帧错误,不能提供身份认证。


35. 最终选型表

需求推荐模式
一个终端向一个服务器上报UDP 单播
服务器向一个终端发命令UDP 单播或 TCP
局域网发现未知设备IPv4 UDP 广播或 IP 组播
一份数据发给多个已加入终端UDP 组播
少量不同IP/端口目标逐个推送UDP 多端口重复单播
同一主机多个程序接收同一IP、多目标端口推送
IPv6 一对多发现IPv6 组播
文件、日志和固件TCP
公网可靠连续数据流TCP 较常见
受控专网实时状态UDP 单播或组播

36. UDP 通信检查清单

  • 使用单播、广播、组播还是多端口重复单播;
  • 多端口模式是否维护完整的 (IP, Port) 目标列表;
  • 每个目标是否需要独立频率、状态和失败统计;
  • IPv4 还是 IPv6;
  • 目标 IP 是否属于正确地址类型;
  • IPv6 链路本地地址是否指定正确接口;
  • 目标端口是否为 1~65535 中的可用端口;
  • 是否与已有服务冲突;
  • 接收端是否正确 bind()
  • 同端口多套接字是否理解复用和分发规则;
  • 组播接收端是否加入正确的组和网卡;
  • 广播发送端是否启用 SO_BROADCAST
  • 路由、NAT 和防火墙是否允许;
  • 消息格式和字节序是否明确;
  • 是否需要序列号、时间戳和认证;
  • 是否避免 IP 分片;
  • 是否处理丢包、乱序和重复;
  • 是否具有协议版本升级方案。

37. 总结

UDP 的三个基础寻址模式是:

单播
→ 一个发送方发给一个明确目标

广播
→ 一个发送方发给IPv4广播域内所有设备

组播
→ 一个发送方发给所有已加入组播组的设备

多端口推送不是第四种 IP 寻址模式,而是:

发送方维护多个(IP, Port)目标
→ 对每个目标分别发送一条UDP数据报

批量发送接口只能提高提交效率,不能让一条数据报同时拥有多个目标。

地址范围的核心结论:

IPv4组播:224.0.0.0/4
IPv6组播:ff00::/8
IPv4受限广播:255.255.255.255
子网广播:由网络前缀和掩码计算
IPv6:没有广播

端口范围的核心结论:

数值范围:0~65535
正常目标服务通常使用:1~65535
0:保留,bind时可用于请求系统自动分配端口

最关键的三句话是:

单播、广播和组播的区别,首先体现在目标 IP 地址类型和接收者范围上。

广播地址由子网决定,组播地址有固定地址块,普通单播地址则必须排除特殊用途地址。

UDP 只负责传输数据报,业务数据仍需要明确的序列化、可靠性和安全协议。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱刷碗的苏泓舒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值