CrossPlatformSocket.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. using System;
  2. using System.IO;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. namespace MQTTnet.Implementations
  8. {
  9. public sealed class CrossPlatformSocket : IDisposable
  10. {
  11. readonly Socket _socket;
  12. NetworkStream _networkStream;
  13. public CrossPlatformSocket(AddressFamily addressFamily)
  14. {
  15. _socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
  16. }
  17. public CrossPlatformSocket()
  18. {
  19. // Having this contructor is important because avoiding the address family as parameter
  20. // will make use of dual mode in the .net framework.
  21. _socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
  22. }
  23. public CrossPlatformSocket(Socket socket)
  24. {
  25. _socket = socket ?? throw new ArgumentNullException(nameof(socket));
  26. _networkStream = new NetworkStream(socket, true);
  27. }
  28. public bool NoDelay
  29. {
  30. get
  31. {
  32. return (int)_socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay) > 0;
  33. }
  34. set
  35. {
  36. _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
  37. }
  38. }
  39. public bool DualMode
  40. {
  41. get
  42. {
  43. return _socket.DualMode;
  44. }
  45. set
  46. {
  47. _socket.DualMode = value;
  48. }
  49. }
  50. public int ReceiveBufferSize
  51. {
  52. get
  53. {
  54. return _socket.ReceiveBufferSize;
  55. }
  56. set
  57. {
  58. _socket.ReceiveBufferSize = value;
  59. }
  60. }
  61. public int SendBufferSize
  62. {
  63. get
  64. {
  65. return _socket.SendBufferSize;
  66. }
  67. set
  68. {
  69. _socket.SendBufferSize = value;
  70. }
  71. }
  72. public EndPoint RemoteEndPoint
  73. {
  74. get
  75. {
  76. return _socket.RemoteEndPoint;
  77. }
  78. }
  79. public bool ReuseAddress
  80. {
  81. get
  82. {
  83. return (int)_socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress) != 0;
  84. }
  85. set
  86. {
  87. _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, value ? 1 : 0);
  88. }
  89. }
  90. public async Task<CrossPlatformSocket> AcceptAsync()
  91. {
  92. try
  93. {
  94. #if NET452 || NET461
  95. var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null).ConfigureAwait(false);
  96. return new CrossPlatformSocket(clientSocket);
  97. #else
  98. var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false);
  99. return new CrossPlatformSocket(clientSocket);
  100. #endif
  101. }
  102. catch (ObjectDisposedException)
  103. {
  104. // This will happen when _socket.EndAccept gets called by Task library but the socket is already disposed.
  105. return null;
  106. }
  107. }
  108. public void Bind(EndPoint localEndPoint)
  109. {
  110. if (localEndPoint is null) throw new ArgumentNullException(nameof(localEndPoint));
  111. _socket.Bind(localEndPoint);
  112. }
  113. public void Listen(int connectionBacklog)
  114. {
  115. _socket.Listen(connectionBacklog);
  116. }
  117. public async Task ConnectAsync(string host, int port, CancellationToken cancellationToken)
  118. {
  119. if (host is null) throw new ArgumentNullException(nameof(host));
  120. try
  121. {
  122. _networkStream?.Dispose();
  123. // Workaround for: https://github.com/dotnet/corefx/issues/24430
  124. using (cancellationToken.Register(() => _socket.Dispose()))
  125. {
  126. cancellationToken.ThrowIfCancellationRequested();
  127. #if NET452 || NET461
  128. await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, host, port, null).ConfigureAwait(false);
  129. #else
  130. await _socket.ConnectAsync(host, port).ConfigureAwait(false);
  131. #endif
  132. _networkStream = new NetworkStream(_socket, true);
  133. }
  134. }
  135. catch (ObjectDisposedException)
  136. {
  137. // This will happen when _socket.EndConnect gets called by Task library but the socket is already disposed.
  138. }
  139. }
  140. public async Task SendAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
  141. {
  142. try
  143. {
  144. #if NET452 || NET461
  145. await Task.Factory.FromAsync(SocketWrapper.BeginSend, _socket.EndSend, new SocketWrapper(_socket, buffer, socketFlags)).ConfigureAwait(false);
  146. #else
  147. await _socket.SendAsync(buffer, socketFlags).ConfigureAwait(false);
  148. #endif
  149. }
  150. catch (ObjectDisposedException)
  151. {
  152. // This will happen when _socket.EndConnect gets called by Task library but the socket is already disposed.
  153. }
  154. }
  155. public async Task<int> ReceiveAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
  156. {
  157. try
  158. {
  159. #if NET452 || NET461
  160. return await Task.Factory.FromAsync(SocketWrapper.BeginReceive, _socket.EndReceive, new SocketWrapper(_socket, buffer, socketFlags)).ConfigureAwait(false);
  161. #else
  162. return await _socket.ReceiveAsync(buffer, socketFlags).ConfigureAwait(false);
  163. #endif
  164. }
  165. catch (ObjectDisposedException)
  166. {
  167. // This will happen when _socket.EndReceive gets called by Task library but the socket is already disposed.
  168. return -1;
  169. }
  170. }
  171. public NetworkStream GetStream()
  172. {
  173. var networkStream = _networkStream;
  174. if (networkStream == null)
  175. {
  176. throw new IOException("The socket is not connected.");
  177. }
  178. return networkStream;
  179. }
  180. public void Dispose()
  181. {
  182. _networkStream?.Dispose();
  183. _socket?.Dispose();
  184. }
  185. #if NET452 || NET461
  186. class SocketWrapper
  187. {
  188. readonly Socket _socket;
  189. readonly ArraySegment<byte> _buffer;
  190. readonly SocketFlags _socketFlags;
  191. public SocketWrapper(Socket socket, ArraySegment<byte> buffer, SocketFlags socketFlags)
  192. {
  193. _socket = socket;
  194. _buffer = buffer;
  195. _socketFlags = socketFlags;
  196. }
  197. public static IAsyncResult BeginSend(AsyncCallback callback, object state)
  198. {
  199. var socketWrapper = (SocketWrapper)state;
  200. return socketWrapper._socket.BeginSend(socketWrapper._buffer.Array, socketWrapper._buffer.Offset, socketWrapper._buffer.Count, socketWrapper._socketFlags, callback, state);
  201. }
  202. public static IAsyncResult BeginReceive(AsyncCallback callback, object state)
  203. {
  204. var socketWrapper = (SocketWrapper)state;
  205. return socketWrapper._socket.BeginReceive(socketWrapper._buffer.Array, socketWrapper._buffer.Offset, socketWrapper._buffer.Count, socketWrapper._socketFlags, callback, state);
  206. }
  207. }
  208. #endif
  209. }
  210. }