You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
}

疑问

  1. 为何C++应用使用Mellanox网卡时会丢包?
  2. Windows下的C++应用使用Mellanox网卡高效处理组播流量,是否需要特定配置或优化?
  3. 为何Wireshark无丢包而C++应用会丢包?
  4. 有哪些排查步骤或解决建议?

已尝试操作

  • 修改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(需重启生效)。
  • 应用代码优化:
    • 将接收线程的耗时操作(内存分配、队列推送)剥离到单独处理线程,减少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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 23:05:56