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

MacOS下.NET 7中UDP Client绑定地址占用异常求助

MacOS下UDP套接字绑定"地址已被占用"问题解决

你的代码在Windows环境下能正常创建多UDP套接字发送广播,但在MacOS 13.4.1(.NET 7.0.306)中执行绑定操作时抛出SocketException,提示"地址已被占用",核心原因是Windows和MacOS在套接字复用选项的行为上存在差异。

问题代码回顾

private static List<IPAddress> GetEndpoints()
{
    List<IPAddress> AddressList = new List<IPAddress>();
    NetworkInterface[] Interfaces = NetworkInterface.GetAllNetworkInterfaces();

    foreach (NetworkInterface I in Interfaces)
    {
        if ((I.NetworkInterfaceType != NetworkInterfaceType.Loopback) && I.OperationalStatus == OperationalStatus.Up)
        {
            foreach (var Unicast in I.GetIPProperties().UnicastAddresses)
            {
                if (Unicast?.Address?.AddressFamily == AddressFamily.InterNetwork)
                {
                    AddressList.Add(Unicast.Address);
                }
            }
        }
    }
    return AddressList;
}

private static void CreateSockets()
{
    List<IPAddress> Endpoints = GetEndpoints();
    foreach (IPAddress A in Endpoints)
    {
        try
        {
            UdpClient Udp = new UdpClient();
            Udp.Client.ReceiveBufferSize = 100000;
            Udp.ExclusiveAddressUse = false;
            Udp.EnableBroadcast = true;
            Udp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            Udp.Client.Bind(new IPEndPoint(A, 44444));  // 抛出异常的行
            ReceiveDataAsync(Udp, ReceiveDataCallback);
            UdpClients.Add(Udp);
            if (UdpClientBuffer.ContainsKey(Udp))
            {
                UdpClientBuffer[Udp] = String.Empty;
            }
            else
            {
                UdpClientBuffer.Add(Udp, String.Empty);
            }
        }
        catch (Exception ex)
        {
            string S = ex.Message;
        }
    }
}

原因分析

  • Windows中,设置ReuseAddress=true和ExclusiveAddressUse=false后,允许多个套接字绑定到同一端口的不同本地IP地址。
  • MacOS的SO_REUSEADDR选项行为不同,默认不允许多个套接字绑定到同一端口的不同IP,必须额外设置SO_REUSEPORT选项才能实现类似Windows的复用逻辑。

解决方案

方案1:添加跨平台的套接字复用选项

修改CreateSockets方法,在绑定前针对MacOS添加SO_REUSEPORT选项,同时调整选项设置顺序(确保绑定前完成所有配置):

private static void CreateSockets()
{
    List<IPAddress> Endpoints = GetEndpoints();
    foreach (IPAddress A in Endpoints)
    {
        try
        {
            UdpClient Udp = new UdpClient();
            Udp.Client.ReceiveBufferSize = 100000;
            Udp.ExclusiveAddressUse = false;
            Udp.EnableBroadcast = true;
            
            // 先设置ReuseAddress,兼容Windows
            Udp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            
            // 针对MacOS/Linux设置ReusePort,允许同一端口绑定多个IP
            if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
            {
                Udp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReusePort, true);
            }
            
            Udp.Client.Bind(new IPEndPoint(A, 44444));
            ReceiveDataAsync(Udp, ReceiveDataCallback);
            UdpClients.Add(Udp);
            // 简化字典赋值逻辑
            UdpClientBuffer[Udp] = string.Empty;
        }
        catch (Exception ex)
        {
            string S = ex.Message;
        }
    }
}

方案2:绑定到IPAddress.Any减少绑定次数

如果不需要单独监听每个网卡的广播,可以直接绑定到IPAddress.Any,一个套接字即可处理所有网卡的广播请求,避免多次绑定同一端口:

private static void CreateSockets()
{
    try
    {
        UdpClient Udp = new UdpClient();
        Udp.Client.ReceiveBufferSize = 100000;
        Udp.ExclusiveAddressUse = false;
        Udp.EnableBroadcast = true;
        Udp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        
        if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
        {
            Udp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReusePort, true);
        }
        
        // 绑定到所有可用IP的44444端口
        Udp.Client.Bind(new IPEndPoint(IPAddress.Any, 44444));
        ReceiveDataAsync(Udp, ReceiveDataCallback);
        UdpClients.Add(Udp);
        UdpClientBuffer[Udp] = string.Empty;
    }
    catch (Exception ex)
    {
        string S = ex.Message;
    }
}

额外排查步骤

如果上述方案无效,先检查是否有其他进程占用44444端口,在Mac终端执行命令:

lsof -i :44444

若有占用进程,可关闭该进程后再测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 15:07:18