UdpSocket.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Security;
  7. using System.Threading.Tasks;
  8. using MediaBrowser.Model.Net;
  9. namespace Emby.Common.Implementations.Net
  10. {
  11. // THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
  12. // Be careful to check any changes compile and work for all platform projects it is shared in.
  13. internal sealed class UdpSocket : DisposableManagedObjectBase, IUdpSocket
  14. {
  15. #region Fields
  16. private System.Net.Sockets.Socket _Socket;
  17. private int _LocalPort;
  18. #endregion
  19. #region Constructors
  20. public UdpSocket(System.Net.Sockets.Socket socket, int localPort, string ipAddress)
  21. {
  22. if (socket == null) throw new ArgumentNullException("socket");
  23. _Socket = socket;
  24. _LocalPort = localPort;
  25. IPAddress ip = null;
  26. if (String.IsNullOrEmpty(ipAddress))
  27. ip = IPAddress.Any;
  28. else
  29. ip = IPAddress.Parse(ipAddress);
  30. _Socket.Bind(new IPEndPoint(ip, _LocalPort));
  31. if (_LocalPort == 0)
  32. _LocalPort = (_Socket.LocalEndPoint as IPEndPoint).Port;
  33. }
  34. #endregion
  35. #region IUdpSocket Members
  36. public Task<ReceivedUdpData> ReceiveAsync()
  37. {
  38. ThrowIfDisposed();
  39. var tcs = new TaskCompletionSource<ReceivedUdpData>();
  40. System.Net.EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0);
  41. var state = new AsyncReceiveState(_Socket, receivedFromEndPoint);
  42. state.TaskCompletionSource = tcs;
  43. #if NETSTANDARD1_6
  44. _Socket.ReceiveFromAsync(new System.ArraySegment<Byte>(state.Buffer), System.Net.Sockets.SocketFlags.None, state.EndPoint)
  45. .ContinueWith((task, asyncState) =>
  46. {
  47. if (task.Status != TaskStatus.Faulted)
  48. {
  49. var receiveState = asyncState as AsyncReceiveState;
  50. receiveState.EndPoint = task.Result.RemoteEndPoint;
  51. ProcessResponse(receiveState, () => task.Result.ReceivedBytes);
  52. }
  53. }, state);
  54. #else
  55. _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, System.Net.Sockets.SocketFlags.None, ref state.EndPoint, new AsyncCallback(this.ProcessResponse), state);
  56. #endif
  57. return tcs.Task;
  58. }
  59. public Task SendTo(byte[] messageData, IpEndPointInfo endPoint)
  60. {
  61. ThrowIfDisposed();
  62. if (messageData == null) throw new ArgumentNullException("messageData");
  63. if (endPoint == null) throw new ArgumentNullException("endPoint");
  64. #if NETSTANDARD1_6
  65. _Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(endPoint.IpAddress.ToString()), endPoint.Port));
  66. return Task.FromResult(true);
  67. #else
  68. var taskSource = new TaskCompletionSource<bool>();
  69. try
  70. {
  71. _Socket.BeginSendTo(messageData, 0, messageData.Length, SocketFlags.None, new System.Net.IPEndPoint(IPAddress.Parse(endPoint.IpAddress.ToString()), endPoint.Port), result =>
  72. {
  73. try
  74. {
  75. _Socket.EndSend(result);
  76. taskSource.TrySetResult(true);
  77. }
  78. catch (SocketException ex)
  79. {
  80. taskSource.TrySetException(ex);
  81. }
  82. catch (ObjectDisposedException ex)
  83. {
  84. taskSource.TrySetException(ex);
  85. }
  86. catch (InvalidOperationException ex)
  87. {
  88. taskSource.TrySetException(ex);
  89. }
  90. catch (SecurityException ex)
  91. {
  92. taskSource.TrySetException(ex);
  93. }
  94. }, null);
  95. }
  96. catch (SocketException ex)
  97. {
  98. taskSource.TrySetException(ex);
  99. }
  100. catch (ObjectDisposedException ex)
  101. {
  102. taskSource.TrySetException(ex);
  103. }
  104. catch (SecurityException ex)
  105. {
  106. taskSource.TrySetException(ex);
  107. }
  108. //_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(endPoint.IPAddress), endPoint.Port));
  109. return taskSource.Task;
  110. #endif
  111. }
  112. #endregion
  113. #region Overrides
  114. protected override void Dispose(bool disposing)
  115. {
  116. if (disposing)
  117. {
  118. var socket = _Socket;
  119. if (socket != null)
  120. socket.Dispose();
  121. }
  122. }
  123. #endregion
  124. #region Private Methods
  125. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions via task methods should be reported by task completion source, so this should be ok.")]
  126. private static void ProcessResponse(AsyncReceiveState state, Func<int> receiveData)
  127. {
  128. try
  129. {
  130. var bytesRead = receiveData();
  131. var ipEndPoint = state.EndPoint as IPEndPoint;
  132. state.TaskCompletionSource.SetResult(
  133. new ReceivedUdpData()
  134. {
  135. Buffer = state.Buffer,
  136. ReceivedBytes = bytesRead,
  137. ReceivedFrom = ToIpEndPointInfo(ipEndPoint)
  138. }
  139. );
  140. }
  141. catch (ObjectDisposedException)
  142. {
  143. state.TaskCompletionSource.SetCanceled();
  144. }
  145. catch (SocketException se)
  146. {
  147. if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown)
  148. state.TaskCompletionSource.SetException(se);
  149. else
  150. state.TaskCompletionSource.SetCanceled();
  151. }
  152. catch (Exception ex)
  153. {
  154. state.TaskCompletionSource.SetException(ex);
  155. }
  156. }
  157. private static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint)
  158. {
  159. if (endpoint == null)
  160. {
  161. return null;
  162. }
  163. return new IpEndPointInfo
  164. {
  165. IpAddress = new IpAddressInfo
  166. {
  167. Address = endpoint.Address.ToString(),
  168. IsIpv6 = endpoint.AddressFamily == AddressFamily.InterNetworkV6
  169. },
  170. Port = endpoint.Port
  171. };
  172. }
  173. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions via task methods should be reported by task completion source, so this should be ok.")]
  174. private void ProcessResponse(IAsyncResult asyncResult)
  175. {
  176. #if NET46
  177. var state = asyncResult.AsyncState as AsyncReceiveState;
  178. try
  179. {
  180. var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.EndPoint);
  181. var ipEndPoint = state.EndPoint as IPEndPoint;
  182. state.TaskCompletionSource.SetResult(
  183. new ReceivedUdpData()
  184. {
  185. Buffer = state.Buffer,
  186. ReceivedBytes = bytesRead,
  187. ReceivedFrom = ToIpEndPointInfo(ipEndPoint)
  188. }
  189. );
  190. }
  191. catch (ObjectDisposedException)
  192. {
  193. state.TaskCompletionSource.SetCanceled();
  194. }
  195. catch (SocketException se)
  196. {
  197. if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown)
  198. state.TaskCompletionSource.SetException(se);
  199. else
  200. state.TaskCompletionSource.SetCanceled();
  201. }
  202. catch (Exception ex)
  203. {
  204. state.TaskCompletionSource.SetException(ex);
  205. }
  206. #endif
  207. }
  208. #endregion
  209. #region Private Classes
  210. private class AsyncReceiveState
  211. {
  212. public AsyncReceiveState(System.Net.Sockets.Socket socket, EndPoint endPoint)
  213. {
  214. this.Socket = socket;
  215. this.EndPoint = endPoint;
  216. }
  217. public EndPoint EndPoint;
  218. public byte[] Buffer = new byte[8192];
  219. public System.Net.Sockets.Socket Socket { get; private set; }
  220. public TaskCompletionSource<ReceivedUdpData> TaskCompletionSource { get; set; }
  221. }
  222. #endregion
  223. }
  224. }