Windows下C++程序用Mellanox网卡接收UDP组播丢包问题
Windows下Mellanox网卡UDP组播接收丢包问题
环境配置
- Windows操作系统
- Mellanox网卡
- C++编写的UDP组播接收程序
- 同一机器安装有Wireshark
现象观察
- 使用Wireshark加入组播组并通过Mellanox网卡接收数据时,无丢包情况
- 运行C++应用通过Mellanox网卡接收组播数据时,会丢失部分数据包
- 同一C++应用在Solarflare网卡下可正常接收所有数据包,无丢包
代码片段
// Initialize Winsock WSADATA wsaData; int result = WSAStartup(MAKEWORD(2, 2), &wsaData); if (result != 0) { std::cerr << "WSAStartup failed with error: " << result << std::endl; return; } // Create socket SOCKET fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd == INVALID_SOCKET) { std::cerr << "socket creation failed with error: " << WSAGetLastError() << std::endl; WSACleanup(); return; } // Allow multiple sockets to use the same port number BOOL yes = TRUE; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes)) == SOCKET_ERROR) { std::cerr << "setsockopt SO_REUSEADDR failed with error: " << WSAGetLastError() << std::endl; closesocket(fd); WSACleanup(); return; } // Set receive buffer size int receiveBufferSize = 655360000; // 250 * 1024 * 1024; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&receiveBufferSize, sizeof(receiveBufferSize)) == SOCKET_ERROR) { std::cerr << "setsockopt SO_RCVBUF failed with error: " << WSAGetLastError() << std::endl; closesocket(fd); WSACleanup(); return; } // Bind to the specified interface and port struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(_interface.c_str()); addr.sin_port = htons(port); if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { std::cerr << "bind failed with error: " << WSAGetLastError() << std::endl; closesocket(fd); WSACleanup(); return; } // Join the multicast group ip_mreq mreq{}; mreq.imr_multiaddr.s_addr = inet_addr(group.c_str()); mreq.imr_interface.s_addr = inet_addr(_interface.c_str()); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) == SOCKET_ERROR) { std::cerr << "setsockopt IP_ADD_MEMBERSHIP failed with error: " << WSAGetLastError() << std::endl; closesocket(fd); WSACleanup(); return; } LOG_INFO << "Successfully created socket IP: " << group << ", PORT: " << port << " & INTERFACE: " << _interface; // Main receiving loop while (true) { char msgbuf[max_length]; int addrlen = sizeof(addr); int nbytes = recvfrom(fd, msgbuf, max_length, 0, (struct sockaddr*)&addr, &addrlen); if (nbytes == SOCKET_ERROR) { std::cerr << "recvfrom failed with error: " << WSAGetLastError() << std::endl; closesocket(fd); WSACleanup(); return; } // Process received packet char* ptr = new char[nbytes]; memcpy(ptr, msgbuf, nbytes); FIN_STRUCT queueMsg; queueMsg.msg = ptr; queueMsg.len = nbytes; multicast_q.push(queueMsg); }
疑问
- 为何C++应用使用Mellanox网卡时会丢包?
- Windows下的C++应用使用Mellanox网卡高效处理组播流量,是否需要特定配置或优化?
- 为何Wireshark无丢包而C++应用会丢包?
- 有哪些排查步骤或解决建议?
已尝试操作
- 修改Mellanox网卡的接收缓冲区
- 该Mellanox网卡在Linux机器上可正常工作
- 修改Windows系统接收缓冲区,但无效果
另外,是否有Mellanox针对Windows的官方支持文档?是否有开发者有过Windows下使用Mellanox网卡的经验?
问题解答
1. 应用丢包原因分析
Mellanox网卡在Windows下的组播处理逻辑和Solarflare存在差异,常见诱因包括:
- 网卡驱动配置不兼容:Mellanox默认开启的硬件卸载特性(如UDP校验和卸载、接收端缩放RSS),若应用未适配,会导致数据包在网卡或内核层被丢弃。
- Socket选项未适配:代码中设置的
SO_RCVBUF可能未实际生效(Windows对缓冲区大小有系统级限制),且缺少Mellanox网卡需要的特定Socket选项配置。 - 接收线程调度延迟:Mellanox硬件性能较高,若应用接收线程被Windows调度器延迟,会导致内核Socket缓冲区溢出丢包;而Wireshark因使用旁路抓包模式,不受此限制。
2. Mellanox网卡Windows下的组播优化配置
需针对性调整以下内容:
- 网卡驱动设置:
- 打开Mellanox网卡属性面板,禁用不必要的卸载特性(如UDP校验和卸载),或确保特性与应用兼容;启用接收端缩放(RSS)并配置合适队列数,分散多核CPU接收压力。
- 在驱动高级设置中调大硬件接收缓冲区,避免硬件层溢出。
- Socket选项优化:
- (Windows 10+)添加
SO_REUSEPORT选项,允许多个Socket绑定同一端口,配合RSS提升接收效率;设置IP_MULTICAST_IF明确指定接收网卡,避免系统自动选择错误路径。 - 调用
getsockopt检查SO_RCVBUF实际生效值,若远小于设置值,修改注册表项HKLM\SYSTEM\CurrentControlSet\Services\Afd\Parameters\DefaultReceiveWindow(需重启生效)。
- (Windows 10+)添加
- 应用代码优化:
- 将接收线程的耗时操作(内存分配、队列推送)剥离到单独处理线程,减少
recvfrom阻塞间隔;改用重叠IO或IOCP模型,提升高并发接收效率。
- 将接收线程的耗时操作(内存分配、队列推送)剥离到单独处理线程,减少
3. Wireshark无丢包而应用丢包的原因
Wireshark采用旁路抓包模式,直接从网卡硬件缓冲区取数,不经过Windows TCP/IP协议栈和Socket缓冲区,因此不受应用层接收延迟或缓冲区大小限制;而应用需要经过协议栈处理,一旦内核Socket缓冲区满,后续数据包会被直接丢弃。
4. 排查步骤与解决建议
- 调用
getsockopt验证SO_RCVBUF实际生效大小,确认缓冲区配置是否到位。 - 在Mellanox驱动中开启接收丢包日志,定位丢包发生在硬件层还是协议栈层。
- 编写仅包含
recvfrom和计数逻辑的测试程序,排除业务逻辑(如队列、内存分配)导致的延迟。 - 安装Mellanox官方最新Windows驱动,旧版本可能存在组播处理bug。
- 用
route print查看Windows组播路由表,确保组播数据包被正确路由到目标网卡。
Mellanox官网支持页面可找到对应型号的Windows用户手册和开发指南,社区中不少开发者反馈过类似问题,核心是适配驱动设置和Socket模型。
内容的提问来源于stack exchange,提问作者Vikash
相关产品推荐
相关产品推荐

