using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Logging; using System; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reactive.Linq; using System.Text; using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.Udp { /// /// Provides a Udp Server /// public class UdpServer : IDisposable { /// /// The _logger /// private readonly ILogger _logger; /// /// The _network manager /// private readonly INetworkManager _networkManager; /// /// The _HTTP server /// private readonly IHttpServer _httpServer; /// /// The _server configuration manager /// private readonly IServerConfigurationManager _serverConfigurationManager; /// /// Initializes a new instance of the class. /// /// The logger. /// The network manager. /// The server configuration manager. /// The HTTP server. public UdpServer(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager, IHttpServer httpServer) { _logger = logger; _networkManager = networkManager; _serverConfigurationManager = serverConfigurationManager; _httpServer = httpServer; } /// /// Raises the event. /// /// The instance containing the event data. private async void OnMessageReceived(UdpMessageReceivedEventArgs e) { const string context = "Server"; var expectedMessage = String.Format("who is MediaBrowser{0}?", context); var expectedMessageBytes = Encoding.UTF8.GetBytes(expectedMessage); if (expectedMessageBytes.SequenceEqual(e.Bytes)) { _logger.Info("Received UDP server request from " + e.RemoteEndPoint); var localAddress = GetLocalIpAddress(); if (!string.IsNullOrEmpty(localAddress)) { // Send a response back with our ip address and port var response = String.Format("MediaBrowser{0}|{1}:{2}", context, GetLocalIpAddress(), _serverConfigurationManager.Configuration.HttpServerPortNumber); await SendAsync(Encoding.UTF8.GetBytes(response), e.RemoteEndPoint); } else { _logger.Warn("Unable to respond to udp request because the local ip address could not be determined."); } } } /// /// Gets the local ip address. /// /// System.String. private string GetLocalIpAddress() { var localAddresses = _networkManager.GetLocalIpAddresses().ToList(); // Cross-check the local ip addresses with addresses that have been received on with the http server var matchedAddress = _httpServer.LocalEndPoints .ToList() .Select(i => i.Split(':').FirstOrDefault()) .Where(i => !string.IsNullOrEmpty(i)) .FirstOrDefault(i => localAddresses.Contains(i, StringComparer.OrdinalIgnoreCase)); // Return the first matched address, if found, or the first known local address return matchedAddress ?? localAddresses.FirstOrDefault(); } /// /// The _udp client /// private UdpClient _udpClient; /// /// Starts the specified port. /// /// The port. public void Start(int port) { _udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, port)); _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); CreateObservable().Subscribe(OnMessageReceived); } /// /// Creates the observable. /// /// IObservable{UdpReceiveResult}. private IObservable CreateObservable() { return Observable.Create(obs => Observable.FromAsync(() => { try { return _udpClient.ReceiveAsync(); } catch (ObjectDisposedException) { return Task.FromResult(new UdpReceiveResult(new byte[]{}, new IPEndPoint(IPAddress.Any, 0))); } catch (Exception ex) { _logger.ErrorException("Error receiving udp message", ex); return Task.FromResult(new UdpReceiveResult(new byte[] { }, new IPEndPoint(IPAddress.Any, 0))); } }) .Subscribe(obs)) .Repeat() .Retry() .Publish() .RefCount(); } /// /// Called when [message received]. /// /// The message. private void OnMessageReceived(UdpReceiveResult message) { if (message.RemoteEndPoint.Port == 0) { return; } var bytes = message.Buffer; OnMessageReceived(new UdpMessageReceivedEventArgs { Bytes = bytes, RemoteEndPoint = message.RemoteEndPoint.ToString() }); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Stops this instance. /// public void Stop() { if (_udpClient != null) { _udpClient.Close(); } } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool dispose) { if (dispose) { Stop(); } } /// /// Sends the async. /// /// The data. /// The ip address. /// The port. /// Task{System.Int32}. /// data public Task SendAsync(string data, string ipAddress, int port) { return SendAsync(Encoding.UTF8.GetBytes(data), ipAddress, port); } /// /// Sends the async. /// /// The bytes. /// The ip address. /// The port. /// Task{System.Int32}. /// bytes public Task SendAsync(byte[] bytes, string ipAddress, int port) { if (bytes == null) { throw new ArgumentNullException("bytes"); } if (string.IsNullOrEmpty(ipAddress)) { throw new ArgumentNullException("ipAddress"); } return _udpClient.SendAsync(bytes, bytes.Length, ipAddress, port); } /// /// Sends the async. /// /// The bytes. /// The remote end point. /// Task. /// /// bytes /// or /// remoteEndPoint /// public async Task SendAsync(byte[] bytes, string remoteEndPoint) { if (bytes == null) { throw new ArgumentNullException("bytes"); } if (string.IsNullOrEmpty(remoteEndPoint)) { throw new ArgumentNullException("remoteEndPoint"); } await _udpClient.SendAsync(bytes, bytes.Length, _networkManager.Parse(remoteEndPoint)).ConfigureAwait(false); _logger.Info("Udp message sent to {0}", remoteEndPoint); } } }