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

树莓派4(Buster系统)下.Net Socket SendTo UDP无响应问题

问题分析:UDP广播搜索在Raspberry Pi 4 Buster上无响应

我使用私有共享类库中的SearchLocalNetwork方法,通过.NET Socket的SendTo发送UDP广播搜索本地网络中的厂商测试盒。该方法在Windows Visual Studio、运行Stretch系统的树莓派3上均可正常工作,但在运行Buster系统的树莓派4上无法收到响应。通过WireShark抓包可见通信存在,但无明显报错。尝试修改sysctl.conf中的缓冲区大小(net.core.rmem_max = 16777216;net.core.wmem_max = 4194304),未解决问题。相关代码如下:

public List<EthernetDevice> SearchLocalNetwork()
{
    var devices = new List<EthernetDevice>();

    foreach (var adapter in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces())
    {
        foreach (var address in adapter.GetIPProperties().UnicastAddresses)
        {
            if (address.Address.AddressFamily != AddressFamily.InterNetwork)
            {
                continue;
            }

            if (IPAddress.IsLoopback(address.Address))
            {
                continue;
            }

            try
            {
                var ethernetSearch = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                ethernetSearch.ExclusiveAddressUse = false;
                               
                var data = new byte[4];
                var bytBuffer = new byte[256];

                var broadcastAddress = address.Address.GetAddressBytes();

                broadcastAddress[3] = 255;
                var newAddress = new IPAddress(broadcastAddress);
                Console.WriteLine("Broadcast Address: {0}", newAddress);

                IPEndPoint responder = new IPEndPoint(IPAddress.Any, 0);
                EndPoint rep = (EndPoint)responder;
                int i, endtime;

                ethernetSearch.EnableBroadcast = true;
                ethernetSearch.ReceiveTimeout = 8000;
                ethernetSearch.ExclusiveAddressUse = false;                                                

                var ep = new IPEndPoint(newAddress, 0x77FE);                        

                for (i = 0; i < 4; data[i++] = 0);
                data[3] = 0xF8;
                               
                ethernetSearch.SendTo(data, ep);                                                

                endtime = Environment.TickCount + 7000;

                while (true)
                {
                    while (Environment.TickCount < endtime && ethernetSearch.Available == 0)
                    {
                        Thread.Sleep(2);
                    }

                    if (ethernetSearch.Available > 0)
                    {
                        ethernetSearch.ReceiveFrom(bytBuffer, ref rep);
                    }
                    else
                    {
                        break;
                    }

                    if (bytBuffer[3] == 0xF9)
                    {
                        var iPAdd = rep.ToString().Substring(0, rep.ToString().IndexOf(':'));

                        var port = bytBuffer[25] * 256 + bytBuffer[24];
                        iPAdd += ":" + port;

                        var isDhcp = false;

                        if (bytBuffer[4] == 0 && bytBuffer[5] == 0 && bytBuffer[6] == 0 && bytBuffer[7] == 0)
                        {
                            isDhcp = true;
                        }

                        var device = new EthernetDevice();
                        device.IPAddress = iPAdd.Split(':')[0];
                        device.Port = port;
                        device.DHCP = isDhcp;
                        device.Gateway = bytBuffer[16] + "." + bytBuffer[17] + "." + bytBuffer[18] + "." +
                                         bytBuffer[19];
                        device.BaudRate = 2;
                        devices.Add(device);

                        endtime = Environment.TickCount + 700;
                    }
                }

                ethernetSearch.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

    return devices;
}

可能的原因(系统+代码层面)

系统层面(Buster的变化)

  • 防火墙规则变更:Buster默认启用ufw防火墙,可能默认拦截了UDP响应包;而Stretch时期默认防火墙未开启或规则宽松。
  • 内核网络栈行为调整:Buster使用的4.19+内核相比Stretch的4.9内核,UDP广播的接收、转发策略有变化,比如对广播包的过滤逻辑更严格。
  • 多网卡路由问题:RPi4通常同时支持以太网和WiFi,广播包可能被发送到错误网卡,或响应包被路由到非监听的网卡接口。
  • 广播地址配置异常:网卡的广播地址在Buster下可能未正确生成,导致发送的广播包无效。

代码层面潜在缺陷

  • 广播地址计算错误:代码手动将IP最后一段改为255,仅适用于/24子网;如果子网掩码不是255.255.255.0,生成的广播地址无效,设备无法收到请求。
  • Socket未绑定指定网卡:使用IPAddress.Any绑定Socket,在多网卡环境下,响应包可能被其他网卡接收,但当前Socket无法捕获。
  • 依赖Socket.Available判断不可靠:Available属性在不同系统内核下的更新时机有差异,Buster内核可能导致该属性无法及时反映待接收的数据包,错过接收时机。
  • 未处理ReceiveFrom返回值:代码直接使用缓冲区数据,未判断实际接收的字节数,若响应包不完整,会导致后续数组访问拿到无效值(比如bytBuffer[3]不是0xF9),过滤掉有效设备。
  • 超时逻辑依赖Environment.TickCount:TickCount在系统运行超过24天后会溢出,Linux环境下用Stopwatch更可靠。

排查与修复步骤

系统侧排查

  1. 检查防火墙状态:执行sudo ufw status,若开启则添加允许UDP端口30718(0x77FE)的规则:sudo ufw allow 30718/udp。
  2. 验证响应包接收:用tcpdump监听目标端口:sudo tcpdump -i any udp port 30718,确认是否收到设备的响应包。
  3. 检查网卡广播配置:执行ip addr show,查看对应网卡的broadcast字段是否正确(比如inet 192.168.1.10/24 brd 192.168.1.255)。
  4. 确认内核参数:执行sysctl net.ipv4.icmp_echo_ignore_broadcasts,确保值为0(允许广播包)。

代码侧优化

  1. 修正广播地址计算:根据子网掩码生成正确的广播地址:
    var subnetMask = address.IPv4Mask;
    if (subnetMask != null)
    {
        var ipBytes = address.Address.GetAddressBytes();
        var maskBytes = subnetMask.GetAddressBytes();
        var broadcastBytes = new byte[4];
        for (int j = 0; j < 4; j++)
        {
            broadcastBytes[j] = (byte)(ipBytes[j] | ~maskBytes[j]);
        }
        var newAddress = new IPAddress(broadcastBytes);
        Console.WriteLine("Broadcast Address: {0}", newAddress);
    }
    
  2. 绑定Socket到当前网卡IP:确保Socket只接收该网卡的响应:
    ethernetSearch.Bind(new IPEndPoint(address.Address, 0));
    
  3. 替换Available判断逻辑:改用ReceiveTimeout结合异常处理,避免依赖不可靠的属性:
    var stopwatch = Stopwatch.StartNew();
    while (stopwatch.ElapsedMilliseconds < 7000)
    {
        try
        {
            int bytesReceived = ethernetSearch.ReceiveFrom(bytBuffer, ref rep);
            if (bytesReceived >= 4 && bytBuffer[3] == 0xF9)
            {
                // 原有设备解析逻辑
                stopwatch.Restart(); // 收到设备后重置超时
            }
        }
        catch (SocketException ex)
        {
            if (ex.SocketErrorCode != SocketError.TimedOut)
            {
                throw;
            }
        }
    }
    
  4. 处理ReceiveFrom返回值:确保数据足够再解析:
    int bytesReceived = ethernetSearch.ReceiveFrom(bytBuffer, ref rep);
    if (bytesReceived < 26) // 需访问到bytBuffer[25],至少需要26字节
    {
        continue;
    }
    

结论

问题是系统配置(防火墙、内核行为)+ 代码兼容性共同导致的:Buster默认防火墙可能拦截了响应包,同时代码中的广播地址计算、Socket绑定逻辑在多网卡/新内核环境下存在缺陷。先排查系统防火墙和广播配置,再优化代码中的兼容性问题,即可解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 22:10:30