UdpServer.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. using MediaBrowser.Common.Net;
  2. using MediaBrowser.Controller.Configuration;
  3. using MediaBrowser.Controller.Net;
  4. using MediaBrowser.Model.Logging;
  5. using System;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Sockets;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. namespace MediaBrowser.Server.Implementations.Udp
  12. {
  13. /// <summary>
  14. /// Provides a Udp Server
  15. /// </summary>
  16. public class UdpServer : IDisposable
  17. {
  18. /// <summary>
  19. /// The _logger
  20. /// </summary>
  21. private readonly ILogger _logger;
  22. /// <summary>
  23. /// The _network manager
  24. /// </summary>
  25. private readonly INetworkManager _networkManager;
  26. /// <summary>
  27. /// The _HTTP server
  28. /// </summary>
  29. private readonly IHttpServer _httpServer;
  30. /// <summary>
  31. /// The _server configuration manager
  32. /// </summary>
  33. private readonly IServerConfigurationManager _serverConfigurationManager;
  34. private bool _isDisposed;
  35. /// <summary>
  36. /// Initializes a new instance of the <see cref="UdpServer" /> class.
  37. /// </summary>
  38. /// <param name="logger">The logger.</param>
  39. /// <param name="networkManager">The network manager.</param>
  40. /// <param name="serverConfigurationManager">The server configuration manager.</param>
  41. /// <param name="httpServer">The HTTP server.</param>
  42. public UdpServer(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager, IHttpServer httpServer)
  43. {
  44. _logger = logger;
  45. _networkManager = networkManager;
  46. _serverConfigurationManager = serverConfigurationManager;
  47. _httpServer = httpServer;
  48. }
  49. /// <summary>
  50. /// Raises the <see cref="E:MessageReceived" /> event.
  51. /// </summary>
  52. /// <param name="e">The <see cref="UdpMessageReceivedEventArgs"/> instance containing the event data.</param>
  53. private async void OnMessageReceived(UdpMessageReceivedEventArgs e)
  54. {
  55. const string context = "Server";
  56. var expectedMessage = String.Format("who is MediaBrowser{0}?", context);
  57. var expectedMessageBytes = Encoding.UTF8.GetBytes(expectedMessage);
  58. if (expectedMessageBytes.SequenceEqual(e.Bytes))
  59. {
  60. _logger.Info("Received UDP server request from " + e.RemoteEndPoint);
  61. var localAddress = GetLocalIpAddress();
  62. if (!string.IsNullOrEmpty(localAddress))
  63. {
  64. // Send a response back with our ip address and port
  65. var response = String.Format("MediaBrowser{0}|{1}:{2}", context, GetLocalIpAddress(), _serverConfigurationManager.Configuration.HttpServerPortNumber);
  66. await SendAsync(Encoding.UTF8.GetBytes(response), e.RemoteEndPoint);
  67. }
  68. else
  69. {
  70. _logger.Warn("Unable to respond to udp request because the local ip address could not be determined.");
  71. }
  72. }
  73. }
  74. /// <summary>
  75. /// Gets the local ip address.
  76. /// </summary>
  77. /// <returns>System.String.</returns>
  78. private string GetLocalIpAddress()
  79. {
  80. var localAddresses = _networkManager.GetLocalIpAddresses().ToList();
  81. // Cross-check the local ip addresses with addresses that have been received on with the http server
  82. var matchedAddress = _httpServer.LocalEndPoints
  83. .ToList()
  84. .Select(i => i.Split(':').FirstOrDefault())
  85. .Where(i => !string.IsNullOrEmpty(i))
  86. .FirstOrDefault(i => localAddresses.Contains(i, StringComparer.OrdinalIgnoreCase));
  87. // Return the first matched address, if found, or the first known local address
  88. return matchedAddress ?? localAddresses.FirstOrDefault();
  89. }
  90. /// <summary>
  91. /// The _udp client
  92. /// </summary>
  93. private UdpClient _udpClient;
  94. /// <summary>
  95. /// Starts the specified port.
  96. /// </summary>
  97. /// <param name="port">The port.</param>
  98. public void Start(int port)
  99. {
  100. _udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, port));
  101. _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
  102. Task.Run(() => StartListening());
  103. }
  104. private async void StartListening()
  105. {
  106. while (!_isDisposed)
  107. {
  108. try
  109. {
  110. var result = await GetResult().ConfigureAwait(false);
  111. OnMessageReceived(result);
  112. }
  113. catch (ObjectDisposedException)
  114. {
  115. break;
  116. }
  117. catch (Exception ex)
  118. {
  119. _logger.ErrorException("Error in StartListening", ex);
  120. }
  121. }
  122. }
  123. private Task<UdpReceiveResult> GetResult()
  124. {
  125. try
  126. {
  127. return _udpClient.ReceiveAsync();
  128. }
  129. catch (ObjectDisposedException)
  130. {
  131. return Task.FromResult(new UdpReceiveResult(new byte[] { }, new IPEndPoint(IPAddress.Any, 0)));
  132. }
  133. catch (Exception ex)
  134. {
  135. _logger.ErrorException("Error receiving udp message", ex);
  136. return Task.FromResult(new UdpReceiveResult(new byte[] { }, new IPEndPoint(IPAddress.Any, 0)));
  137. }
  138. }
  139. /// <summary>
  140. /// Called when [message received].
  141. /// </summary>
  142. /// <param name="message">The message.</param>
  143. private void OnMessageReceived(UdpReceiveResult message)
  144. {
  145. if (message.RemoteEndPoint.Port == 0)
  146. {
  147. return;
  148. }
  149. var bytes = message.Buffer;
  150. OnMessageReceived(new UdpMessageReceivedEventArgs
  151. {
  152. Bytes = bytes,
  153. RemoteEndPoint = message.RemoteEndPoint.ToString()
  154. });
  155. }
  156. /// <summary>
  157. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  158. /// </summary>
  159. public void Dispose()
  160. {
  161. Dispose(true);
  162. GC.SuppressFinalize(this);
  163. }
  164. /// <summary>
  165. /// Stops this instance.
  166. /// </summary>
  167. public void Stop()
  168. {
  169. _isDisposed = true;
  170. if (_udpClient != null)
  171. {
  172. _udpClient.Close();
  173. }
  174. }
  175. /// <summary>
  176. /// Releases unmanaged and - optionally - managed resources.
  177. /// </summary>
  178. /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  179. protected virtual void Dispose(bool dispose)
  180. {
  181. if (dispose)
  182. {
  183. Stop();
  184. }
  185. }
  186. /// <summary>
  187. /// Sends the async.
  188. /// </summary>
  189. /// <param name="data">The data.</param>
  190. /// <param name="ipAddress">The ip address.</param>
  191. /// <param name="port">The port.</param>
  192. /// <returns>Task{System.Int32}.</returns>
  193. /// <exception cref="System.ArgumentNullException">data</exception>
  194. public Task SendAsync(string data, string ipAddress, int port)
  195. {
  196. return SendAsync(Encoding.UTF8.GetBytes(data), ipAddress, port);
  197. }
  198. /// <summary>
  199. /// Sends the async.
  200. /// </summary>
  201. /// <param name="bytes">The bytes.</param>
  202. /// <param name="ipAddress">The ip address.</param>
  203. /// <param name="port">The port.</param>
  204. /// <returns>Task{System.Int32}.</returns>
  205. /// <exception cref="System.ArgumentNullException">bytes</exception>
  206. public Task SendAsync(byte[] bytes, string ipAddress, int port)
  207. {
  208. if (bytes == null)
  209. {
  210. throw new ArgumentNullException("bytes");
  211. }
  212. if (string.IsNullOrEmpty(ipAddress))
  213. {
  214. throw new ArgumentNullException("ipAddress");
  215. }
  216. return _udpClient.SendAsync(bytes, bytes.Length, ipAddress, port);
  217. }
  218. /// <summary>
  219. /// Sends the async.
  220. /// </summary>
  221. /// <param name="bytes">The bytes.</param>
  222. /// <param name="remoteEndPoint">The remote end point.</param>
  223. /// <returns>Task.</returns>
  224. /// <exception cref="System.ArgumentNullException">
  225. /// bytes
  226. /// or
  227. /// remoteEndPoint
  228. /// </exception>
  229. public async Task SendAsync(byte[] bytes, string remoteEndPoint)
  230. {
  231. if (bytes == null)
  232. {
  233. throw new ArgumentNullException("bytes");
  234. }
  235. if (string.IsNullOrEmpty(remoteEndPoint))
  236. {
  237. throw new ArgumentNullException("remoteEndPoint");
  238. }
  239. await _udpClient.SendAsync(bytes, bytes.Length, _networkManager.Parse(remoteEndPoint)).ConfigureAwait(false);
  240. _logger.Info("Udp message sent to {0}", remoteEndPoint);
  241. }
  242. }
  243. }