From 5f30069520be0ce48873e08e19169a35d12a40e9 Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Thu, 24 Apr 2025 14:41:51 +0200 Subject: [PATCH] Improved broadcast and local ip address determination --- Unity/SiteServer.cs | 4 +- Unity/Thing.cs | 1 - src/Participants/ParticipantUDP.cs | 81 +++++++++++++++++++----------- src/Participants/SiteServer.cs | 29 ++++++++++- 4 files changed, 84 insertions(+), 31 deletions(-) diff --git a/Unity/SiteServer.cs b/Unity/SiteServer.cs index 3a686fe..83e9870 100644 --- a/Unity/SiteServer.cs +++ b/Unity/SiteServer.cs @@ -6,6 +6,8 @@ using UnityEngine; namespace RoboidControl.Unity { public class SiteServer : MonoBehaviour { + public int port = 7681; + public RoboidControl.SiteServer site; public Queue thingQueue = new(); @@ -13,7 +15,7 @@ namespace RoboidControl.Unity { protected virtual void Awake() { Console.SetOut(new UnityLogWriter()); - site = new RoboidControl.SiteServer(7681); + site = new RoboidControl.SiteServer(port); RoboidControl.Thing.OnNewThing += HandleNewThing; } diff --git a/Unity/Thing.cs b/Unity/Thing.cs index 457f893..306e021 100644 --- a/Unity/Thing.cs +++ b/Unity/Thing.cs @@ -1,6 +1,5 @@ #if UNITY_5_3_OR_NEWER using System.Collections; -using Mono.Cecil.Cil; using UnityEngine; using UnityEngine.Networking; diff --git a/src/Participants/ParticipantUDP.cs b/src/Participants/ParticipantUDP.cs index 5df9e19..440d552 100644 --- a/src/Participants/ParticipantUDP.cs +++ b/src/Participants/ParticipantUDP.cs @@ -1,8 +1,8 @@ using System; -using System.Collections.Generic; using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; +using System.Net.NetworkInformation; namespace RoboidControl { @@ -46,8 +46,35 @@ namespace RoboidControl { this.isIsolated = true; else this.remoteSite = new Participant(ipAddress, port); + Participant.AddParticipant(this); + // Determine local IP address + IPAddress localIpAddress = null; + IPAddress subnetMask = null; + using (Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, 0)) { + // Connect to a remote endpoint — we won't actually send anything + socket.Connect("1.1.1.1", 65530); + if (socket.LocalEndPoint is IPEndPoint endPoint) { + localIpAddress = endPoint.Address; + this.ipAddress = localIpAddress.ToString(); + } + } + // Find subnet mask + foreach (NetworkInterface nwInterface in NetworkInterface.GetAllNetworkInterfaces()) { + if (nwInterface.OperationalStatus != OperationalStatus.Up) + continue; + + foreach (UnicastIPAddressInformation unicastAddress in nwInterface.GetIPProperties().UnicastAddresses) { + if (unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork && unicastAddress.Address.Equals(localIpAddress) + ) { + subnetMask = unicastAddress.IPv4Mask; + } + } + } + if (localIpAddress != null && subnetMask != null) + GetBroadcastAddress(localIpAddress, subnetMask); + this.endPoint = new IPEndPoint(IPAddress.Any, localPort); this.udpClient = new UdpClient(localPort); this.udpClient.BeginReceive(new AsyncCallback(result => ReceiveUDP(result)), null); @@ -64,34 +91,31 @@ namespace RoboidControl { this.port = port; } + protected void GetBroadcastAddress(IPAddress ip, IPAddress subnetMask) { + byte[] ipBytes = ip.GetAddressBytes(); + byte[] maskBytes = subnetMask.GetAddressBytes(); + + if (ipBytes.Length != maskBytes.Length) + throw new ArgumentException("IP address and subnet mask lengths do not match"); + + byte[] broadcastBytes = new byte[ipBytes.Length]; + for (int i = 0; i < ipBytes.Length; i++) { + broadcastBytes[i] = (byte)(ipBytes[i] | (~maskBytes[i])); + } + + IPAddress broadcastAddress = new(broadcastBytes); + this.broadcastIpAddress = broadcastAddress.ToString(); + + Console.WriteLine($"Subnet mask: {subnetMask.ToString()}"); + Console.WriteLine($"Broadcast address: {this.broadcastIpAddress}"); + } + private static ParticipantUDP isolatedParticipant = null; public static ParticipantUDP Isolated() { if (isolatedParticipant == null) isolatedParticipant = new ParticipantUDP(0); return isolatedParticipant; } - - // public List owners = new List(); - - // public Participant GetParticipant(string ipAddress, int port) { - // //Console.WriteLine($"Get Participant {ipAddress}:{port}"); - // foreach (Participant sender in owners) { - // if (sender.ipAddress == ipAddress && sender.port == port) - // return sender; - // } - // return null; - // } - // public Participant AddParticipant(string ipAddress, int port) { - // Console.WriteLine($"New Participant {ipAddress}:{port}"); - // Participant participant = new(ipAddress, port) { - // networkId = (byte)(this.owners.Count + 1) - // }; - // owners.Add(participant); - // return participant; - // } - - // protected readonly Dictionary> thingMsgProcessors = new(); - #endregion Init #region Update @@ -161,7 +185,6 @@ namespace RoboidControl { if (this.isIsolated) continue; - //foreach (Thing thing in participant.things) { for (int thingIx = 0; thingIx < participant.things.Count; thingIx++) { Thing thing = participant.things[thingIx]; if (thing == null) @@ -212,7 +235,7 @@ namespace RoboidControl { if (bufferSize <= 0) return true; - Console.WriteLine($"publish to {broadcastIpAddress.ToString()} {this.port}"); + // Console.WriteLine($"publish to {broadcastIpAddress.ToString()} {this.port}"); this.udpClient?.Send(this.buffer, bufferSize, this.broadcastIpAddress, this.port); return true; } @@ -252,10 +275,12 @@ namespace RoboidControl { // We can receive our own publish (broadcast) packages. How do we recognize them???? // It is hard to determine our source port string ipAddress = endPoint.Address.ToString(); - Participant remoteParticipant = GetParticipant(ipAddress, endPoint.Port); - remoteParticipant ??= AddParticipant(ipAddress, endPoint.Port); + if (ipAddress != this.ipAddress) { + Participant remoteParticipant = GetParticipant(ipAddress, endPoint.Port); + remoteParticipant ??= AddParticipant(ipAddress, endPoint.Port); - ReceiveData(data, remoteParticipant); + ReceiveData(data, remoteParticipant); + } udpClient.BeginReceive(new AsyncCallback(callbackResult => ReceiveUDP(callbackResult)), null); } diff --git a/src/Participants/SiteServer.cs b/src/Participants/SiteServer.cs index 9e36f7c..0e39d83 100644 --- a/src/Participants/SiteServer.cs +++ b/src/Participants/SiteServer.cs @@ -1,6 +1,7 @@ using System; using System.Net; using System.Net.Sockets; +using System.Net.NetworkInformation; namespace RoboidControl { @@ -17,6 +18,32 @@ namespace RoboidControl { this.name = "Site Server"; Participant.AddParticipant(this); + // Determine local IP address + IPAddress localIpAddress = null; + IPAddress subnetMask = null; + using (Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, 0)) { + // Connect to a remote endpoint — we won't actually send anything + socket.Connect("1.1.1.1", 65530); + if (socket.LocalEndPoint is IPEndPoint endPoint) { + localIpAddress = endPoint.Address; + this.ipAddress = localIpAddress.ToString(); + } + } + // Find subnet mask + foreach (NetworkInterface nwInterface in NetworkInterface.GetAllNetworkInterfaces()) { + if (nwInterface.OperationalStatus != OperationalStatus.Up) + continue; + + foreach (UnicastIPAddressInformation unicastAddress in nwInterface.GetIPProperties().UnicastAddresses) { + if (unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork && unicastAddress.Address.Equals(localIpAddress) + ) { + subnetMask = unicastAddress.IPv4Mask; + } + } + } + if (localIpAddress != null && subnetMask != null) + GetBroadcastAddress(localIpAddress, subnetMask); + Console.Write($"Prepare receive on port {port}"); this.endPoint = new IPEndPoint(IPAddress.Any, port); this.udpClient = new UdpClient(port); @@ -77,7 +104,7 @@ namespace RoboidControl { Console.WriteLine($"SiteServer: Process thing [{msg.networkId}/{msg.thingId}] {msg.thingType} {msg.parentId} "); Thing thing = sender.Get(msg.networkId, msg.thingId); if (thing == null) - thing = new Thing(sender, msg.networkId, msg.thingId, msg.thingType); + thing = new Thing(sender, msg.networkId, msg.thingId, msg.thingType); if (msg.parentId != 0) { thing.parent = sender.Get(msg.networkId, msg.parentId);