适用对象:网络通信、嵌入式开发、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 655350≤Port≤65535
其中:
- PortPortPort:UDP 端口号;
- 0:保留端口;
- 65535:最大端口号。
IANA 将端口分为三段:
| 范围 | 名称 | 含义 |
|---|---|---|
0~1023 | System Ports | 系统端口或知名端口 |
1024~49151 | User Ports | 用户端口或注册端口 |
49152~65535 | Dynamic/Private Ports | 动态端口或私有端口 |
3.1 端口 0 怎样理解
端口 0 被保留,通常不作为正常服务的目标端口。
需要区分两种情况:
- UDP 首部中的源端口字段为 0:协议格式表示发送方没有提供可用源端口;
- 套接字执行
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/16 | IPv4 链路本地地址 | 通常不跨路由器 |
224.0.0.0/4 | IPv4 组播地址 | 不是普通单播地址 |
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 广播发送方和接收方怎样准备
发送方需要:
- 创建 IPv4 UDP 套接字;
- 启用
SO_BROADCAST; - 确定广播地址;
- 指定接收端共同监听的端口;
- 调用
sendto()。
接收方需要:
- 创建 UDP 套接字;
- 绑定约定端口;
- 通常绑定
0.0.0.0; - 配置防火墙;
- 调用
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 组播发送方怎样准备
发送方通常需要:
- 创建 UDP 套接字;
- 设置目标组播 IP 和端口;
- 选择发送网卡;
- 设置 TTL 或 Hop Limit;
- 调用
sendto()。
发送方通常不需要加入组播组,除非它自己也需要接收该组数据。
8.4 组播接收方怎样准备
接收方通常需要:
- 创建 UDP 套接字;
- 绑定约定端口;
- 指定本地网卡;
- 加入组播组;
- 调用
recvfrom()。
IPv4 常使用:
IP_ADD_MEMBERSHIP
IPv6 常使用:
IPV6_JOIN_GROUP
仅仅绑定端口并不一定足够,接收方还必须加入目标组播组。
组播接收实际上需要同时满足三项:
组播组地址匹配
+
接收网卡匹配
+
UDP目标端口匹配
IP_ADD_MEMBERSHIP 或 IPV6_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_REUSEADDR 或 SO_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 多端口推送流程图
10. 单播、广播、组播和多端口推送对比
| 对比项 | 单播 | IPv4 广播 | IP 组播 | 多端口重复单播 |
|---|---|---|---|---|
| 通信关系 | 一对一 | 一对广播域内全部主机 | 一对已加入组的接收者 | 一个程序对多个端点逐一发送 |
| IPv4 | 支持 | 支持 | 支持 | 支持 |
| IPv6 | 支持 | 不支持 | 支持 | 支持 |
| 目标地址 | 普通单播地址 | 受限或子网广播地址 | IPv4 224.0.0.0/4;IPv6 ff00::/8 | 多个普通 (IP, Port) |
| 一次数据报的目标数 | 1 | 1 个广播地址 | 1 个组播地址 | 每条数据报仍为 1 |
| 是否通常跨路由器 | 可以 | 默认不可以 | 取决于组播路由配置 | 可以 |
| 接收方特殊准备 | 绑定端口 | 绑定端口 | 绑定端口并加入组播组 | 各目标分别绑定对应端口 |
| 发送端出口副本 | 1 | 1 | 1 | 约等于目标端点数 |
| 典型场景 | 终端上报服务器 | 局域网发现 | 一份数据分发多个终端 | 少量异构目标逐个推送 |
多端口推送不是新的 IP 寻址模式,而是使用单播对多个目标端点循环发送的应用层策略。
11. 三种通信模式流程图
第二部分: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()
→ 校验
→ 反序列化
接收方通常需要:
- 创建
SOCK_DGRAM套接字; - 绑定本地 IP 和端口;
- 配置防火墙;
- 调用
recvfrom()或recvmsg(); - 检查来源、长度和截断状态;
- 解析应用层消息。
绑定:
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 地址。
测试步骤:
- 检查双方 IP、掩码和网关;
- 使用
ping初步检查 IP 连通性; - 使用 Wireshark 或 tcpdump 抓包;
- 检查程序绑定的 IP 和端口;
- 检查防火墙。
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) 不能解决什么
它只能减少填充,不能解决:
- 字节序;
- 类型宽度;
- 浮点格式;
enum和bool表示;- 版本兼容;
- 指针成员;
- 非对齐访问;
- 编译器扩展差异。
因此,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=65535−20−8=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=65535−8=65527 bytes
理论最大值不等于推荐长度。
以 MTU 为 1500 字节为例:
LpayloadIPv4≤1500−20−8=1472 bytesL_{\mathrm{payload}}^{\mathrm{IPv4}} \le 1500-20-8 = 1472\ \text{bytes}LpayloadIPv4≤1500−20−8=1472 bytes
LpayloadIPv6≤1500−40−8=1452 bytesL_{\mathrm{payload}}^{\mathrm{IPv6}} \le 1500-40-8 = 1452\ \text{bytes}LpayloadIPv6≤1500−40−8=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 的区别
| 对比项 | UDP | TCP |
|---|---|---|
| 服务形式 | 数据报 | 字节流 |
| 建立通信 | 不握手 | 需要建立连接 |
| 消息边界 | 保留 | 不保留 |
| 可靠送达 | 不保证 | 向对端 TCP 协议栈提供可靠、有序字节流 |
| 顺序 | 不保证 | 保证字节顺序 |
| 重传 | 应用自行设计 | TCP 自动处理 |
| 流量控制 | 无 TCP 式机制 | 有 |
| 拥塞控制 | 应用需要负责 | 有 |
| 广播/组播 | 可结合 IP 广播和组播 | 不支持原生广播和组播 |
| 典型接口 | sendto/recvfrom | connect/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 只负责传输数据报,业务数据仍需要明确的序列化、可靠性和安全协议。

1万+

被折叠的 条评论
为什么被折叠?



