商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > C++实现简单回声服务器Echo Server _ UDP协议Socket开发【源码】

C++实现简单回声服务器Echo Server _ UDP协议Socket开发【源码】

  发布于2026-07-18 阅读(0)

扫一扫,手机访问

UDP回声服务器的开发里,有一个特别容易踩的坑——bind之后直接调用recvfrom,结果返回-1errno被置成EINVAL。这通常不是因为代码逻辑写错了,而是地址结构体没初始化到位。

C++实现简单回声服务器Echo Server _ UDP协议Socket开发【源码】

UDP回声服务器为什么bind后不能直接recvfrom

问题出在两点:要么没指定接收缓冲区大小,要么地址结构体没初始化。具体来说,传入一个未用memset清零的sockaddr_in结构,其中sin_zero字段残留的垃圾值,会导致系统校验失败,直接返回EINVAL

  • sockaddr_in必须用memset(&addr, 0, sizeof(addr))初始化,只赋值sin_portsin_addr远远不够
  • addrlen参数必须初始化为sizeof(addr),而且每次调用recvfrom前都要重置——这个值会被函数修改,不重置下次就出问题
  • UDP是无连接的,recvfrom返回的是发送方地址,不是服务器本机地址;别把addr当作服务监听地址来用,这是另一个常见误解

如何避免recvfrom阻塞导致整个服务卡死

默认socket是阻塞模式,recvfrom会一直等数据到来。在简单回声服务中这还凑合,但一旦网络异常,进程就卡住了,连Ctrl+C都响应不了。更稳妥的做法是设为非阻塞或加超时。

  • fcntl(sockfd, F_SETFL, O_NONBLOCK)设为非阻塞,然后检查recvfrom返回值:若为-1errno等于EAGAINEWOULDBLOCK,说明暂无数据,继续循环即可
  • 也可以用setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))设置接收超时,tvstruct timeval,比如{.tv_sec = 1, .tv_usec = 0}
  • Windows下SO_RCVTIMEOtimeval成员名相同但行为略有差异,跨平台的话,优先推荐非阻塞配合selectpoll来做

sendto失败常见原因和调试方法

从经验来看,sendto返回-1时,90%的情况是目标地址不对或socket状态异常。UDP不保证送达,但内核至少要能完成路由判断,否则立刻报错。

  • 最常踩的坑:传给sendtosockaddr_in*指针指向了局部变量,而这个变量在recvfrom后已出作用域(比如在if块里声明),导致地址内容被覆盖
  • 检查errno:若为EADDRNOTA VAIL,说明对方IP不在本地直连网段且无路由;若为EHOSTUNREACH,通常是目标主机关机或防火墙拦截
  • tcpdump -i any udp port 抓包确认:是否收到请求?是否发出响应?响应目的IP和端口是否与请求源一致?——这些细节往往能直接定位问题

为什么不用fork或线程也能支持多客户端

UDP本身无连接,每个recvfrom都带完整源地址,sendto可直接回发给对应客户端。不需要像TCP那样靠fork或线程维护独立连接上下文。

  • 单线程UDP回声服务器天然支持并发:只要不阻塞在recvfrom,就能轮询处理多个客户端发来的包
  • 注意不要复用同一个sockaddr_in变量存不同客户端地址——每次recvfrom后应立即用它构造sendto的目标地址
  • 如果要做“广播式回声”,比如向所有历史客户端转发,才需要自己维护客户端地址列表;纯echo场景完全无需状态保存

真正容易被忽略的是地址复用(SO_REUSEADDR)和IPv4/IPv6兼容性。开发时绑定INADDR_ANY但没设SO_REUSEADDR,改代码重启服务常遇到Address already in use;而硬写AF_INET会无法响应IPv6客户端发来的IPv4-mapped地址包。这些细节看着不起眼,但碰上了特别折腾人。

本文转载于:https://www.php.cn/faq/2339137.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注