C++怎样实现基于UDP协议的简易客户端服务端双向通信

作者:袖梨 2026-06-19
UDP客户端需绑定本地端口(可设0自动分配)并用sendto()向服务端准确IP和端口发送数据,否则数据被静默丢弃;服务端用recvfrom()接收并原样地址回包,无需connect或维护连接状态。

UDP 客户端如何正确绑定并发送数据到服务端

UDP 是无连接协议,客户端不需要显式 connect(),但必须确保目标地址和端口准确;否则发出去的数据包会被内核静默丢弃,且不会报错。常见错误是误用 INADDR_ANY 或本地未绑定端口,导致服务端回包时无法送达(因为内核找不到对应 socket)。

实操建议:

立即学习“C++免费学习笔记(深入)”;

  • 客户端调用 bind() 绑定一个本地端口(哪怕用 0 让系统自动分配),否则 recvfrom() 会失败或收不到回包
  • 发送前务必用 inet_addr()inet_pton() 检查 IP 字符串合法性,非法地址会导致 sendto() 返回 -1 且 errnoEINVAL
  • 目标端口必须和服务端监听端口一致;若服务端监听 8080,客户端却发到 8081,数据就彻底丢失
  • sendto() 时,addrlen 参数必须是 sizeof(sockaddr_in),传小了会导致地址截断,服务端看到的是乱码 IP

UDP 服务端如何安全处理并发收发而不阻塞

单线程 UDP 服务端通常用 recvfrom() 阻塞等待,但这不等于“不能双向通信”——关键在于:每个收到的包都携带源地址信息,服务端可直接用该地址+端口调用 sendto() 回复,无需维护连接状态。

实操建议:

立即学习“C++免费学习笔记(深入)”;

  • 不要在循环里重复 bind();绑定一次即可,重复调用会返回 errno = EADDRINUSE
  • 每次 recvfrom() 后,把拿到的 sockaddr_in 结构体原样传给 sendto(),不要重新构造或修改 sin_port 字段(注意网络字节序已由 recvfrom() 自动填充)
  • 若需支持多客户端同时通信,不必开多线程;只要每次回复都用收到的源地址,天然支持“伪并发”
  • 避免在 sendto() 前对同一 sockaddr_in 调用 htonl()htons() —— 这些字段已是网络序,二次转换会导致端口变成 0

如何避免 recvfrom() 收不到回包或触发 EAGAIN

recvfrom() 默认阻塞,但若设了 SOCK_NONBLOCKO_NONBLOCK,没数据时会立即返回 -1 并置 errno = EAGAINEWOULDBLOCK。这不是错误,而是预期行为;盲目重试或忽略它,会导致 CPU 空转或逻辑跳过有效数据。

实操建议:

立即学习“C++免费学习笔记(深入)”;

  • 检查 recvfrom() 返回值:等于 -1 且 errno == EAGAIN 时,应继续下一轮循环,而非报错退出
  • 缓冲区大小至少设为 65536(即 UDP 最大理论包长),小于 65507 可能导致大数据包被截断,且不提示
  • 收到数据后,立刻用 ntohl()/ntohs() 解析源 IP 和端口——别依赖打印调试,因为 printf("%d", sin_port) 会显示错误的主机序数值
  • Windows 下需调用 WSAStartup() 初始化,否则所有 socket 函数均失败,错误码为 WSANOTINITIALISED

跨平台编译时 sockaddr_in 和头文件的典型坑

Linux 和 Windows 对 sockaddr_in 的定义基本一致,但头文件依赖和初始化方式差异明显。最常踩的坑是漏包含、顺序错、或忘记清理资源。

实操建议:

立即学习“C++免费学习笔记(深入)”;

  • Linux 必须包含:<sys/socket.h><netinet/in.h><arpa/inet.h>;Windows 必须包含:<winsock2.h>,且 #include <ws2tcpip.h> 在其后
  • Windows 下必须在程序开头调用 WSAStartup(MAKEWORD(2,2), &wsaData),结尾调用 WSACleanup();漏掉任一都会导致后续 socket 创建失败
  • memset(&addr, 0, sizeof(addr)) 必须在声明 sockaddr_in addr 后立即执行,否则 sin_zero 字段可能含垃圾值,影响地址比较或 sendto() 行为
  • 关闭 socket 时,Linux 用 close(),Windows 用 closesocket();混用会导致句柄泄漏或崩溃
实际跑通的关键不在“双向”二字,而在于客户端是否绑定了本地端口、服务端是否用收到的地址原样回包、以及跨平台时是否严格区分初始化和清理流程。少一个环节,通信就静默失败。

相关文章

精彩推荐