You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
6.5 KiB

  1. #if !NET20
  2. using System;
  3. using System.IO;
  4. using System.Net;
  5. using System.Net.Security;
  6. using System.Net.Sockets;
  7. using System.Security.Authentication;
  8. using System.Security.Cryptography.X509Certificates;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. namespace Apewer.WebSocket
  12. {
  13. internal class SocketWrapper
  14. {
  15. public const UInt32 KeepAliveInterval = 60000;
  16. public const UInt32 RetryInterval = 10000;
  17. private readonly Socket _socket;
  18. private Stream _stream;
  19. private CancellationTokenSource _tokenSource;
  20. private TaskFactory _taskFactory;
  21. public string RemoteIpAddress
  22. {
  23. get
  24. {
  25. var endpoint = _socket.RemoteEndPoint as IPEndPoint;
  26. return endpoint != null ? endpoint.Address.ToString() : null;
  27. }
  28. }
  29. public int RemotePort
  30. {
  31. get
  32. {
  33. var endpoint = _socket.RemoteEndPoint as IPEndPoint;
  34. return endpoint != null ? endpoint.Port : -1;
  35. }
  36. }
  37. public void SetKeepAlive(Socket socket, UInt32 keepAliveInterval, UInt32 retryInterval)
  38. {
  39. int size = sizeof(UInt32);
  40. UInt32 on = 1;
  41. byte[] inArray = new byte[size * 3];
  42. Array.Copy(BitConverter.GetBytes(on), 0, inArray, 0, size);
  43. Array.Copy(BitConverter.GetBytes(keepAliveInterval), 0, inArray, size, size);
  44. Array.Copy(BitConverter.GetBytes(retryInterval), 0, inArray, size * 2, size);
  45. socket.IOControl(IOControlCode.KeepAliveValues, inArray, null);
  46. }
  47. public SocketWrapper(Socket socket)
  48. {
  49. _tokenSource = new CancellationTokenSource();
  50. _taskFactory = new TaskFactory(_tokenSource.Token);
  51. _socket = socket;
  52. if (_socket.Connected)
  53. _stream = new NetworkStream(_socket);
  54. // The tcp keepalive default values on most systems
  55. // are huge (~7200s). Set them to something more reasonable.
  56. if (Runtime.IsRunningOnWindows())
  57. {
  58. SetKeepAlive(socket, KeepAliveInterval, RetryInterval);
  59. }
  60. }
  61. public Task Authenticate(X509Certificate2 certificate, SslProtocols enabledSslProtocols, Action callback, Action<Exception> error)
  62. {
  63. var ssl = new SslStream(_stream, false);
  64. _stream = new QueuedStream(ssl);
  65. Func<AsyncCallback, object, IAsyncResult> begin =
  66. (cb, s) => ssl.BeginAuthenticateAsServer(certificate, false, enabledSslProtocols, false, cb, s);
  67. Task task = Task.Factory.FromAsync(begin, ssl.EndAuthenticateAsServer, null);
  68. task.ContinueWith(t => callback(), TaskContinuationOptions.NotOnFaulted)
  69. .ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
  70. task.ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
  71. return task;
  72. }
  73. public void Listen(int backlog)
  74. {
  75. _socket.Listen(backlog);
  76. }
  77. public void Bind(EndPoint endPoint)
  78. {
  79. _socket.Bind(endPoint);
  80. }
  81. public bool Connected
  82. {
  83. get { return _socket.Connected; }
  84. }
  85. public Stream Stream
  86. {
  87. get { return _stream; }
  88. }
  89. public bool NoDelay
  90. {
  91. get { return _socket.NoDelay; }
  92. set { _socket.NoDelay = value; }
  93. }
  94. public EndPoint LocalEndPoint
  95. {
  96. get { return _socket.LocalEndPoint; }
  97. }
  98. public Task<int> Receive(byte[] buffer, Action<int> callback, Action<Exception> error, int offset = 0)
  99. {
  100. try
  101. {
  102. Func<AsyncCallback, object, IAsyncResult> begin =
  103. (cb, s) => _stream.BeginRead(buffer, offset, buffer.Length, cb, s);
  104. Task<int> task = Task.Factory.FromAsync<int>(begin, _stream.EndRead, null);
  105. task.ContinueWith(t => callback(t.Result), TaskContinuationOptions.NotOnFaulted)
  106. .ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
  107. task.ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
  108. return task;
  109. }
  110. catch (Exception e)
  111. {
  112. error(e);
  113. return null;
  114. }
  115. }
  116. public Task<SocketWrapper> Accept(Action<SocketWrapper> callback, Action<Exception> error)
  117. {
  118. Func<IAsyncResult, SocketWrapper> end = r => _tokenSource.Token.IsCancellationRequested ? null : new SocketWrapper(_socket.EndAccept(r));
  119. var task = _taskFactory.FromAsync(_socket.BeginAccept, end, null);
  120. task.ContinueWith(t => callback(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
  121. .ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
  122. task.ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
  123. return task;
  124. }
  125. public void Dispose()
  126. {
  127. _tokenSource.Cancel();
  128. if (_stream != null) _stream.Dispose();
  129. if (_socket != null) _socket.Dispose();
  130. }
  131. public void Close()
  132. {
  133. _tokenSource.Cancel();
  134. if (_stream != null) _stream.Close();
  135. if (_socket != null) _socket.Close();
  136. }
  137. public int EndSend(IAsyncResult asyncResult)
  138. {
  139. _stream.EndWrite(asyncResult);
  140. return 0;
  141. }
  142. public Task Send(byte[] buffer, Action callback, Action<Exception> error)
  143. {
  144. if (_tokenSource.IsCancellationRequested)
  145. return null;
  146. try
  147. {
  148. Func<AsyncCallback, object, IAsyncResult> begin =
  149. (cb, s) => _stream.BeginWrite(buffer, 0, buffer.Length, cb, s);
  150. Task task = Task.Factory.FromAsync(begin, _stream.EndWrite, null);
  151. task.ContinueWith(t => callback(), TaskContinuationOptions.NotOnFaulted)
  152. .ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
  153. task.ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
  154. return task;
  155. }
  156. catch (Exception e)
  157. {
  158. error(e);
  159. return null;
  160. }
  161. }
  162. }
  163. }
  164. #endif