using Google.Protobuf; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Timers; using Tango.PMR.Discovery; namespace Tango.Transport.Discovery { public class UdpDiscoveryClient : IDiscoveryClient where DiscoveryMessage : IMessage { private Thread _receiveThread; /// /// Occurs when a matching service has been discovered. /// public event EventHandler> ServiceDiscovered; /// /// Gets or sets the interval in which the discovery message will be sent. /// public TimeSpan Interval { get; set; } /// /// Gets a value indicating whether this service has been started. /// public bool IsStarted { get; private set; } /// /// Gets the UDP port number. /// public int Port { get; private set; } /// /// Prevents a default instance of the class from being created. /// private UdpDiscoveryClient() { Interval = TimeSpan.FromSeconds(5); } /// /// Initializes a new instance of the class. /// /// The UDP port number. public UdpDiscoveryClient(int port) : this() { Port = port; } /// /// Starts the discovery client. /// public void Start() { if (!IsStarted) { IsStarted = true; _receiveThread = new Thread(ReceiveThreadLoop); _receiveThread.IsBackground = true; _receiveThread.Start(); } } /// /// Stops the discovery client. /// public void Stop() { if (IsStarted) { IsStarted = false; } } /// /// Handles the Elapsed event of the _timer control. /// /// The source of the event. /// The instance containing the event data. private void ReceiveThreadLoop() { UdpClient udpClient = new UdpClient(Port); udpClient.Client.ReceiveTimeout = (int)Interval.TotalMilliseconds; var endPoint = new IPEndPoint(IPAddress.Any, Port); while (IsStarted) { try { byte[] data = null; do { data = udpClient.Receive(ref endPoint); if (IsStarted) { if (data != null && data.Length > 0) { DiscoveryMessage message = Activator.CreateInstance(); var parser = message.GetParser(); message = (DiscoveryMessage)parser.ParseFrom(data); var host = Dns.GetHostEntry(endPoint.Address); string address = endPoint.Address.ToString(); ServiceDiscovered?.Invoke(this, new DiscoveredService( address, host != null ? host.HostName : "Unresolved", message , () => { try { TcpClient client = new TcpClient(); client.Connect(address, Port); client.Dispose(); return true; } catch { return false; } })); } } } while (IsStarted && data != null && data.Length > 0); } catch { } if (!IsStarted) break; Thread.Sleep(Interval); } udpClient.Close(); } /// /// Asynchronous method for awaiting until the service will be discovered. /// /// /// public Task> Discover(TimeSpan? timeout = null) { Start(); TaskCompletionSource> source = new TaskCompletionSource>(); EventHandler> handler = null; handler = (sender, e) => { ServiceDiscovered -= handler; source.SetResult(e); }; ServiceDiscovered += handler; Task.Delay(timeout != null ? timeout.Value : TimeSpan.FromSeconds(10)).ContinueWith((x) => { if (!source.Task.IsCompleted) { source.SetException(new TimeoutException()); } }); return source.Task; } } }