Unix环境下如何让同端口多UDP套接字均接收广播同步消息
问题:Unix环境下UDP多进程监听同一端口,实现所有进程接收消息
我希望在Unix环境下使用UDP同步不同进程,所有需要同步的进程将监听同一端口。我在macOS上尝试了以下C#代码,但未成功——目前仅有一个套接字能接收消息,似乎存在负载均衡分发机制,请问如何让所有套接字都接收到消息?
using System.Net; using System.Net.Sockets; async Task Listen (){ try { IPEndPoint localpt = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4000); UdpClient udpServer = new UdpClient(); udpServer.ExclusiveAddressUse = false; udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); udpServer.Client.EnableBroadcast = true; udpServer.Client.Bind(localpt); Console.WriteLine("listening"); while (true) { var msg = await udpServer.ReceiveAsync(); Console.WriteLine("Datagram received on UDP socket " + localpt); } } catch (Exception e) { Console.WriteLine(e.Message); } } void Send() { byte[] SYNC_DATAGRAM = "SYNC"u8.ToArray(); var client = new UdpClient(); client.EnableBroadcast = true; client.Send(SYNC_DATAGRAM, SYNC_DATAGRAM.Length, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4000)); } Listen(); Listen(); Thread.Sleep(1000); Send(); Console.Read();
解决方案
在macOS(BSD系Unix系统)中,SO_REUSEADDR仅允许多个套接字绑定同一端口,但内核会通过负载均衡策略将UDP数据包分发给其中一个套接字,无法实现所有进程接收消息的需求。要让所有绑定同一端口的UDP套接字都收到全部数据包,必须使用SO_REUSEPORT套接字选项。
修改后的代码
将原代码中的SocketOptionName.ReuseAddress替换为SocketOptionName.ReusePort即可:
using System.Net; using System.Net.Sockets; using System.Threading; async Task Listen (){ try { IPEndPoint localpt = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4000); UdpClient udpServer = new UdpClient(); udpServer.ExclusiveAddressUse = false; // 替换为SO_REUSEPORT选项 udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReusePort, true); udpServer.Client.EnableBroadcast = true; udpServer.Client.Bind(localpt); Console.WriteLine($"Process {Environment.ProcessId} listening on {localpt}"); while (true) { var msg = await udpServer.ReceiveAsync(); Console.WriteLine($"Process {Environment.ProcessId} received datagram: {System.Text.Encoding.ASCII.GetString(msg.Buffer)}"); } } catch (Exception e) { Console.WriteLine(e.Message); } } void Send() { byte[] SYNC_DATAGRAM = "SYNC"u8.ToArray(); var client = new UdpClient(); client.EnableBroadcast = true; client.Send(SYNC_DATAGRAM, SYNC_DATAGRAM.Length, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4000)); } Listen(); Listen(); Thread.Sleep(1000); Send(); Console.Read();
原理说明
SO_REUSEPORT在BSD系系统(包括macOS)中的核心行为是:允许多个独立进程(或同一进程内的多个套接字)绑定到完全相同的IP地址和端口,并且内核会将每个UDP数据包复制一份,发送给所有绑定的套接字。这正好满足你需要所有同步进程都接收同一消息的需求。
注意:所有需要接收消息的进程都必须设置SO_REUSEPORT选项,并且保持ExclusiveAddressUse = false,否则无法成功绑定同一端口。
内容的提问来源于stack exchange,提问作者Daniel Argüelles
相关产品推荐
相关产品推荐

