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
相关产品推荐
相关产品推荐

