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

为何调用发送1个UDP包时程序实际多发?如何控制发包数量?

UDP发包异常:单次调用send_to却多发数据包的问题及解决思路

问题描述

我编写的简单UDP程序本应只发送1个UDP包,但WireShark显示有3个相同的数据包突发发送,接收端仅收到2个。

需要解决两个问题:

  • 如何让它只发送1个?应用程序理应能控制发包数量,并非所有程序都会出现这种情况,我忽略了什么?
  • 这种特性在某些场景下可能有用:有没有办法让它主动发送2个或4个?

测试代码

//
// How many messages does this send out?
//
// g++ -std=c++20 howmany.cpp -o howmany.exe -lws2_32
//
// nc -u -p 15000 -l
//

#include <iostream>
#include <boost/asio.hpp>

using namespace std;

namespace asio = boost::asio;
using udp = asio::ip::udp;

static constexpr string localBindAddr{"10.0.0.53"};

void HowManyMsgs()
{
    asio::io_context ioCtxt;
    
    udp::endpoint localEP{ asio::ip::address::from_string(localBindAddr), 15000};
    udp::socket skt{ioCtxt, localEP};
    //udp_socket_await skt{ioCtxt, localEP};
    skt.set_option(udp::socket::reuse_address(true));
    //skt.set_option(asio::ip::unicast::hops(7)); // TTL

    udp::endpoint dest{ asio::ip::address::from_string("10.0.0.140"), 15000};
    string hello{"Hello there destination.\n"};
    size_t nSent = skt.send_to(asio::const_buffer{hello.data(), hello.size()}, dest);
    cout << "Sent once: " << nSent << " bytes to " << dest;
    sleep(1);
}


int main()
{
    HowManyMsgs();
    return 0;
}

抓包与接收结果

Wireshark抓包

No. Time    Source  Destination Protocol    Length  Info
65  1.537746    10.0.0.53   10.0.0.140  UDP 67  15000 → 15000 Len=25
66  1.537755    10.0.0.53   10.0.0.140  UDP 67  15000 → 15000 Len=25
67  1.537772    10.0.0.53   10.0.0.140  UDP 67  15000 → 15000 Len=25

接收端(nc命令)输出

# nc -u -p 15000 -l
Hello there destination.
Hello there destination.

运行环境

基于Win32的boost::asio(msys2.org /mingw64)


更新:问题根源定位

这似乎是VMWare Workstation网络栈导致的异常现象。测试场景:

  • 在VM宿主机运行程序,通过桥接网络向本机VM虚拟机发送消息时,出现多发数据包;
  • 关闭VM虚拟机后,向局域网内另一台机器发送,仅发送1个数据包;
  • 重新开启VM虚拟机后,向局域网内其他机器发送时会出现4个相同的数据包。

可见VMWare网络适配器是关键因素,该问题属于适配器配置范畴。


问题分析与解决思路

  1. 多发数据包的原因
    你的代码逻辑完全正确,单次调用send_to只会触发一次系统调用,发送一个UDP包。多发是VMWare虚拟网络适配器的特殊转发机制导致的,当宿主机与虚拟机通过桥接网络通信时,虚拟网络栈可能会复制数据包进行内部转发,从而出现重复发包的情况。

  2. 确保只发送1个数据包的方法

    • 切换网络环境:在物理机之间通信,或使用VMWare的NAT网络模式替代桥接模式;
    • 调整VMWare网络配置:打开VMWare虚拟网络编辑器,重置虚拟网络设置,或关闭可能导致数据包复制的优化选项;
    • 无需修改代码:代码本身没有问题,问题出在底层网络栈。
  3. 主动控制发送指定数量的数据包
    如果需要发送2个、4个等指定数量的UDP包,直接在代码中多次调用send_to即可,这是可靠且可控的方式,示例代码如下:

    void HowManyMsgs()
    {
        asio::io_context ioCtxt;
        
        udp::endpoint localEP{ asio::ip::address::from_string(localBindAddr), 15000};
        udp::socket skt{ioCtxt, localEP};
        skt.set_option(udp::socket::reuse_address(true));
    
        udp::endpoint dest{ asio::ip::address::from_string("10.0.0.140"), 15000};
        string hello{"Hello there destination.\n"};
        
        // 主动发送2个数据包
        const int sendCount = 2;
        for (int i = 0; i < sendCount; ++i) {
            size_t nSent = skt.send_to(asio::const_buffer{hello.data(), hello.size()}, dest);
            cout << "Sent " << (i+1) << ": " << nSent << " bytes to " << dest << endl;
            // 可选:添加微延迟避免数据包被网络栈合并
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
        sleep(1);
    }
    

    不要依赖底层网络栈的异常多发行为,这种行为不可控且受环境影响极大。


内容的提问来源于stack exchange,提问作者JimB

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 06:23:22