Session.NET.cs 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. using System;
  2. using System.Net.Sockets;
  3. using Renci.SshNet.Abstractions;
  4. namespace Renci.SshNet
  5. {
  6. public partial class Session
  7. {
  8. #if FEATURE_SOCKET_POLL
  9. /// <summary>
  10. /// Gets a value indicating whether the socket is connected.
  11. /// </summary>
  12. /// <param name="isConnected"><c>true</c> if the socket is connected; otherwise, <c>false</c></param>
  13. /// <remarks>
  14. /// <para>
  15. /// As a first check we verify whether <see cref="Socket.Connected"/> is
  16. /// <c>true</c>. However, this only returns the state of the socket as of
  17. /// the last I/O operation.
  18. /// </para>
  19. /// <para>
  20. /// Therefore we use the combination of <see cref="Socket.Poll(int, SelectMode)"/> with mode <see cref="SelectMode.SelectRead"/>
  21. /// and <see cref="Socket.Available"/> to verify if the socket is still connected.
  22. /// </para>
  23. /// <para>
  24. /// The MSDN doc mention the following on the return value of <see cref="Socket.Poll(int, SelectMode)"/>
  25. /// with mode <see cref="SelectMode.SelectRead"/>:
  26. /// <list type="bullet">
  27. /// <item>
  28. /// <description><c>true</c> if data is available for reading;</description>
  29. /// </item>
  30. /// <item>
  31. /// <description><c>true</c> if the connection has been closed, reset, or terminated; otherwise, returns <c>false</c>.</description>
  32. /// </item>
  33. /// </list>
  34. /// </para>
  35. /// <para>
  36. /// <c>Conclusion:</c> when the return value is <c>true</c> - but no data is available for reading - then
  37. /// the socket is no longer connected.
  38. /// </para>
  39. /// <para>
  40. /// When a <see cref="Socket"/> is used from multiple threads, there's a race condition
  41. /// between the invocation of <see cref="Socket.Poll(int, SelectMode)"/> and the moment
  42. /// when the value of <see cref="Socket.Available"/> is obtained. As a workaround, we signal
  43. /// when bytes are read from the <see cref="Socket"/>.
  44. /// </para>
  45. /// </remarks>
  46. #else
  47. /// <summary>
  48. /// Gets a value indicating whether the socket is connected.
  49. /// </summary>
  50. /// <param name="isConnected"><c>true</c> if the socket is connected; otherwise, <c>false</c></param>
  51. /// <remarks>
  52. /// We verify whether <see cref="Socket.Connected"/> is <c>true</c>. However, this only returns the state
  53. /// of the socket as of the last I/O operation.
  54. /// </remarks>
  55. #endif
  56. partial void IsSocketConnected(ref bool isConnected)
  57. {
  58. DiagnosticAbstraction.Log(string.Format("[{0}] {1} Checking socket", ToHex(SessionId), DateTime.Now.Ticks));
  59. lock (_socketDisposeLock)
  60. {
  61. #if FEATURE_SOCKET_POLL
  62. if (_socket == null || !_socket.Connected)
  63. {
  64. isConnected = false;
  65. return;
  66. }
  67. lock (_socketReadLock)
  68. {
  69. var connectionClosedOrDataAvailable = _socket.Poll(1, SelectMode.SelectRead);
  70. isConnected = !(connectionClosedOrDataAvailable && _socket.Available == 0);
  71. }
  72. #else
  73. isConnected = _socket != null && _socket.Connected;
  74. #endif // FEATURE_SOCKET_POLL
  75. }
  76. DiagnosticAbstraction.Log(string.Format("[{0}] {1} Checked socket", ToHex(SessionId), DateTime.Now.Ticks));
  77. }
  78. }
  79. }