DeviceDiscovery.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. using MediaBrowser.Common.Events;
  2. using MediaBrowser.Controller;
  3. using MediaBrowser.Controller.Configuration;
  4. using MediaBrowser.Controller.Dlna;
  5. using MediaBrowser.Model.Logging;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Net.Sockets;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. using MediaBrowser.Common.Net;
  14. namespace MediaBrowser.Dlna.Ssdp
  15. {
  16. public class DeviceDiscovery : IDeviceDiscovery, IDisposable
  17. {
  18. private bool _disposed;
  19. private readonly ILogger _logger;
  20. private readonly IServerConfigurationManager _config;
  21. private SsdpHandler _ssdpHandler;
  22. private readonly CancellationTokenSource _tokenSource;
  23. private readonly IServerApplicationHost _appHost;
  24. public event EventHandler<SsdpMessageEventArgs> DeviceDiscovered;
  25. public event EventHandler<SsdpMessageEventArgs> DeviceLeft;
  26. private readonly INetworkManager _networkManager;
  27. public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, IServerApplicationHost appHost, INetworkManager networkManager)
  28. {
  29. _tokenSource = new CancellationTokenSource();
  30. _logger = logger;
  31. _config = config;
  32. _appHost = appHost;
  33. _networkManager = networkManager;
  34. }
  35. private List<IPAddress> GetLocalIpAddresses()
  36. {
  37. return _networkManager.GetLocalIpAddresses().ToList();
  38. }
  39. public void Start(SsdpHandler ssdpHandler)
  40. {
  41. _ssdpHandler = ssdpHandler;
  42. _ssdpHandler.MessageReceived += _ssdpHandler_MessageReceived;
  43. foreach (var localIp in GetLocalIpAddresses())
  44. {
  45. try
  46. {
  47. CreateListener(localIp);
  48. }
  49. catch (Exception e)
  50. {
  51. _logger.ErrorException("Failed to Initilize Socket", e);
  52. }
  53. }
  54. }
  55. void _ssdpHandler_MessageReceived(object sender, SsdpMessageEventArgs e)
  56. {
  57. string nts;
  58. e.Headers.TryGetValue("NTS", out nts);
  59. if (String.Equals(e.Method, "NOTIFY", StringComparison.OrdinalIgnoreCase) &&
  60. String.Equals(nts, "ssdp:byebye", StringComparison.OrdinalIgnoreCase) &&
  61. !_disposed)
  62. {
  63. EventHelper.FireEventIfNotNull(DeviceLeft, this, e, _logger);
  64. return;
  65. }
  66. try
  67. {
  68. if (e.LocalEndPoint == null)
  69. {
  70. var ip = _appHost.LocalIpAddresses.FirstOrDefault(i => !IPAddress.IsLoopback(i));
  71. if (ip != null)
  72. {
  73. e.LocalEndPoint = new IPEndPoint(ip, 0);
  74. }
  75. }
  76. if (e.LocalEndPoint != null)
  77. {
  78. TryCreateDevice(e);
  79. }
  80. }
  81. catch (OperationCanceledException)
  82. {
  83. }
  84. catch (Exception ex)
  85. {
  86. _logger.ErrorException("Error creating play to controller", ex);
  87. }
  88. }
  89. private void CreateListener(IPAddress localIp)
  90. {
  91. Task.Factory.StartNew(async (o) =>
  92. {
  93. try
  94. {
  95. _logger.Info("Creating SSDP listener on {0}", localIp);
  96. var endPoint = new IPEndPoint(localIp, 1900);
  97. using (var socket = GetMulticastSocket(localIp, endPoint))
  98. {
  99. var receiveBuffer = new byte[64000];
  100. CreateNotifier(localIp);
  101. while (!_tokenSource.IsCancellationRequested)
  102. {
  103. var receivedBytes = await socket.ReceiveAsync(receiveBuffer, 0, 64000);
  104. if (receivedBytes > 0)
  105. {
  106. var args = SsdpHelper.ParseSsdpResponse(receiveBuffer);
  107. args.EndPoint = endPoint;
  108. args.LocalEndPoint = new IPEndPoint(localIp, 0);
  109. _ssdpHandler.LogMessageReceived(args, true);
  110. TryCreateDevice(args);
  111. }
  112. }
  113. }
  114. _logger.Info("SSDP listener - Task completed");
  115. }
  116. catch (OperationCanceledException)
  117. {
  118. }
  119. catch (Exception e)
  120. {
  121. _logger.ErrorException("Error in listener", e);
  122. }
  123. }, _tokenSource.Token, TaskCreationOptions.LongRunning);
  124. }
  125. private void CreateNotifier(IPAddress localIp)
  126. {
  127. Task.Factory.StartNew(async (o) =>
  128. {
  129. try
  130. {
  131. while (true)
  132. {
  133. _ssdpHandler.SendSearchMessage(new IPEndPoint(localIp, 1900));
  134. var delay = _config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds * 1000;
  135. await Task.Delay(delay, _tokenSource.Token).ConfigureAwait(false);
  136. }
  137. }
  138. catch (OperationCanceledException)
  139. {
  140. }
  141. catch (Exception ex)
  142. {
  143. _logger.ErrorException("Error in notifier", ex);
  144. }
  145. }, _tokenSource.Token, TaskCreationOptions.LongRunning);
  146. }
  147. private Socket GetMulticastSocket(IPAddress localIpAddress, EndPoint localEndpoint)
  148. {
  149. var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  150. socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
  151. socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIpAddress));
  152. socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
  153. socket.Bind(localEndpoint);
  154. return socket;
  155. }
  156. private void TryCreateDevice(SsdpMessageEventArgs args)
  157. {
  158. string nts;
  159. args.Headers.TryGetValue("NTS", out nts);
  160. if (String.Equals(nts, "ssdp:byebye", StringComparison.OrdinalIgnoreCase))
  161. {
  162. if (String.Equals(args.Method, "NOTIFY", StringComparison.OrdinalIgnoreCase))
  163. {
  164. if (!_disposed)
  165. {
  166. EventHelper.FireEventIfNotNull(DeviceLeft, this, args, _logger);
  167. }
  168. }
  169. return;
  170. }
  171. string usn;
  172. if (!args.Headers.TryGetValue("USN", out usn)) usn = string.Empty;
  173. string nt;
  174. if (!args.Headers.TryGetValue("NT", out nt)) nt = string.Empty;
  175. // Need to be able to download device description
  176. string location;
  177. if (!args.Headers.TryGetValue("Location", out location) ||
  178. string.IsNullOrEmpty(location))
  179. {
  180. return;
  181. }
  182. EventHelper.FireEventIfNotNull(DeviceDiscovered, this, args, _logger);
  183. }
  184. public void Dispose()
  185. {
  186. if (_ssdpHandler != null)
  187. {
  188. _ssdpHandler.MessageReceived -= _ssdpHandler_MessageReceived;
  189. }
  190. if (!_disposed)
  191. {
  192. _disposed = true;
  193. _tokenSource.Cancel();
  194. }
  195. }
  196. }
  197. }