using Google.Protobuf; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using System.Timers; using Tango.Core; using Tango.Core.ExtensionMethods; using Tango.Logging; using Tango.PMR.Discovery; using Tango.PMR.Integration; using Tango.Transport.Helpers; using Tango.Transport.Servers; namespace Tango.Transport.Discovery { /// /// Represents UDP discovery service broadcasting a discovery message of type . /// /// public class UdpDiscoveryService : ExtendedObject, IDiscoveryService where DiscoveryMessage : IMessage { private Timer _timer; private TcpServer _tcpValidationServer; /// /// Occurs before broadcasting the discovery message and gives a chance to modify the packet message. /// public event EventHandler BeforeBroadcasting; /// /// Gets or sets the current discovery message. /// public DiscoveryMessage CurrentDiscoveryMessage { get; set; } /// /// Gets or sets the TCP validation information. /// public TcpValidationInfo TcpValidationInfo { get; set; } /// /// Gets or sets the multi-cast group address when using multi-cast discovery method. /// public String MulticastGroupAddress { get; set; } /// /// 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 UdpDiscoveryService() { TcpValidationInfo = new TcpValidationInfo(); Interval = TimeSpan.FromSeconds(5); CurrentDiscoveryMessage = Activator.CreateInstance(); } /// /// Initializes a new instance of the class. /// /// The port. public UdpDiscoveryService(int port) : this() { Port = port; _tcpValidationServer = new TcpServer(Port); _tcpValidationServer.ClientConnected += (x, e) => { try { var data = GenericMessageSerializer.Serialize(TcpValidationInfo, GenericMessageProtocol.Bson); e.Socket.GetStream().Write(data, 0, data.Length); e.Socket.Dispose(); } catch (Exception ex) { LogManager.Log(ex, "TCP Validation Server Response Error."); } }; } /// /// Initializes a new instance of the class. /// /// The port. /// The discovery message. public UdpDiscoveryService(int port, DiscoveryMessage discoveryMessage) : this(port) { CurrentDiscoveryMessage = discoveryMessage; } /// /// Starts the discovery service. /// public void Start() { if (!IsStarted) { _tcpValidationServer.Start(); _timer = new Timer(); _timer.Interval = Interval.TotalMilliseconds; _timer.Elapsed += _timer_Elapsed; _timer.Enabled = true; _timer.Start(); IsStarted = true; } } /// /// Stops the discovery service. /// public void Stop() { if (IsStarted) { _tcpValidationServer.Stop(); //Transmit the discovery packet one more time so clients can tell that we have disconnected. BroadcastDiscoveryPacket(); _timer.Stop(); IsStarted = false; } } /// /// Handles the Elapsed event of the _timer control. /// /// The source of the event. /// The instance containing the event data. private void _timer_Elapsed(object sender, ElapsedEventArgs e) { BroadcastDiscoveryPacket(); } private void BroadcastDiscoveryPacket() { //Broadcast try { BeforeBroadcasting?.Invoke(this, CurrentDiscoveryMessage); UdpClient client = new UdpClient(); IPEndPoint endPoint = new IPEndPoint(IPAddress.Broadcast, Port); byte[] bytes = CurrentDiscoveryMessage.ToByteArray(); client.EnableBroadcast = true; client.Send(bytes, bytes.Length, endPoint); client.Close(); } catch (Exception ex) { LogManager.Log(ex, "Error broadcasting discovery packet."); } //Multicast try { UdpClient client = new UdpClient(AddressFamily.InterNetwork); IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(MulticastGroupAddress), Port); client.JoinMulticastGroup(IPAddress.Parse(MulticastGroupAddress), IPAddress.Parse(IPAddressHelper.GetLocalIpAddress())); byte[] bytes = CurrentDiscoveryMessage.ToByteArray(); client.Client.Ttl = 10; client.Send(bytes, bytes.Length, endPoint); client.Close(); } catch (Exception ex) { LogManager.Log(ex, LogCategory.Debug, "Error multicasting discovery packet."); } } } }