UDP socket绑定失败主因是TIME_WAIT残留,需在bind前设SO_REUSEADDR;recvfrom失败常因sockaddr_in未初始化;sendto成功不保证送达,受MTU限制(以太网约1472字节);跨平台需条件编译处理Winsock与POSIX差异。

UDP socket 创建失败:bind 时提示 Address already in use
这通常不是端口真被占了,而是上一次程序崩溃后没正确关闭 socket,系统还保留着 TIME_WAIT 状态。Linux 下默认会等 2MSL(约 60 秒),直接复用会报错。
解决办法是设置 SO_REUSEADDR 选项:
int opt = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
- 必须在
bind()之前调用,否则无效 - Windows 下也支持,但行为略有差异;macOS 需额外加
SO_REUSEPORT才能多进程绑定同一端口 - 别把它当成“万能解”,只是避免开发时反复改端口号
recvfrom 收不到数据:检查 sockaddr_in 地址结构初始化
新手常漏掉 memset(&addr, 0, sizeof(addr)),导致 sin_port 或 sin_addr 是随机值,recvfrom 可能返回 0 字节或直接失败。
更稳妥的写法是用 sockaddr_in{} 直接零初始化:
立即学习“C++免费学习笔记(深入)”;
struct sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
-
INADDR_ANY表示监听所有网卡,不是 “0.0.0.0” 字符串 - 发送端若用
INADDR_LOOPBACK(即 127.0.0.1),接收端也得监听该地址,否则收不到 - IPv6 要换用
sockaddr_in6和AF_INET6,混用会静默失败
sendto 发送成功但对方收不到:注意缓冲区大小和 MTU 限制
UDP 单包理论最大 65535 字节,但实际受链路层 MTU 限制。以太网默认 MTU 是 1500,减去 IP 头(20)+ UDP 头(8),有效载荷上限约 1472 字节。超长包会被静默丢弃,且不通知发送方。
- 用
sendto()返回值判断是否“发送成功”——它只表示内核收下了,不代表对方收到 - 超过 1472 字节建议分包,自己加序号和校验,别依赖 IP 层分片(中间设备可能丢片)
- 局域网测试可用
ping -s 1472 192.168.x.x验证路径 MTU
跨平台编译失败:WSAStartup 和 closesocket 在 Linux 不可用
Windows 的 socket API 是 Winsock,Linux/macOS 是 POSIX socket,头文件、初始化、关闭方式都不同。
最简适配方式是条件编译:
#ifdef _WIN32 #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") #else #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #define closesocket close #endif
- Windows 必须先调
WSAStartup(),否则所有 socket 函数返回失败 - Linux 下
close()即可,closesocket()是未定义符号 - 错误码也不一样:
WSAGetLastError()vserrno,别统一用strerror(errno)
sendto/recvfrom 都要传地址结构;看似简单,但地址复用、字节序、缓冲区边界、平台差异这些点,一个没对齐就卡半天。











