mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 17:25:51 +00:00
76a92cfb0a
- Remove ZeroReaderIndex from SshData; offset should instead be specified when instance is created/loaded. - LoadBytes is now private, and is always invoked when a message is loaded. - Eliminate ResetReader(). SshDataStream.cs: - Added overload taking buffer, offset and count. KeyExchange*Message.cs: - Remove ResetReader() calls. Message.cs: - Remove ZeroReaderIndex override. SftpMessage.cs: - Pass offset and count to Load overload to allow skipping byte representing the message type. - Remove ZeroReaderIndex override. SftpSession.cs: - Avoid buffering when packet contains full SFTP response message. Session.cs: - Pass offset and count to Load overload to allow skipping offset bytes. General: - Temporarily add (too) verbose tracing.
98 lines
4.4 KiB
C#
98 lines
4.4 KiB
C#
using System;
|
|
using System.Net.Sockets;
|
|
using Renci.SshNet.Abstractions;
|
|
|
|
namespace Renci.SshNet
|
|
{
|
|
public partial class Session
|
|
{
|
|
#if FEATURE_SOCKET_POLL
|
|
/// <summary>
|
|
/// Holds the lock object to ensure read access to the socket is synchronized.
|
|
/// </summary>
|
|
private readonly object _socketReadLock = new object();
|
|
#endif // FEATURE_SOCKET_POLL
|
|
|
|
#if FEATURE_SOCKET_POLL
|
|
/// <summary>
|
|
/// Gets a value indicating whether the socket is connected.
|
|
/// </summary>
|
|
/// <param name="isConnected"><c>true</c> if the socket is connected; otherwise, <c>false</c></param>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// As a first check we verify whether <see cref="Socket.Connected"/> is
|
|
/// <c>true</c>. However, this only returns the state of the socket as of
|
|
/// the last I/O operation.
|
|
/// </para>
|
|
/// <para>
|
|
/// Therefore we use the combination of <see cref="Socket.Poll(int, SelectMode)"/> with mode <see cref="SelectMode.SelectRead"/>
|
|
/// and <see cref="Socket.Available"/> to verify if the socket is still connected.
|
|
/// </para>
|
|
/// <para>
|
|
/// The MSDN doc mention the following on the return value of <see cref="Socket.Poll(int, SelectMode)"/>
|
|
/// with mode <see cref="SelectMode.SelectRead"/>:
|
|
/// <list type="bullet">
|
|
/// <item>
|
|
/// <description><c>true</c> if data is available for reading;</description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <description><c>true</c> if the connection has been closed, reset, or terminated; otherwise, returns <c>false</c>.</description>
|
|
/// </item>
|
|
/// </list>
|
|
/// </para>
|
|
/// <para>
|
|
/// <c>Conclusion:</c> when the return value is <c>true</c> - but no data is available for reading - then
|
|
/// the socket is no longer connected.
|
|
/// </para>
|
|
/// <para>
|
|
/// When a <see cref="Socket"/> is used from multiple threads, there's a race condition
|
|
/// between the invocation of <see cref="Socket.Poll(int, SelectMode)"/> and the moment
|
|
/// when the value of <see cref="Socket.Available"/> is obtained. As a workaround, we signal
|
|
/// when bytes are read from the <see cref="Socket"/>.
|
|
/// </para>
|
|
/// </remarks>
|
|
#else
|
|
/// <summary>
|
|
/// Gets a value indicating whether the socket is connected.
|
|
/// </summary>
|
|
/// <param name="isConnected"><c>true</c> if the socket is connected; otherwise, <c>false</c></param>
|
|
/// <remarks>
|
|
/// We verify whether <see cref="Socket.Connected"/> is <c>true</c>. However, this only returns the state
|
|
/// of the socket as of the last I/O operation.
|
|
/// </remarks>
|
|
#endif
|
|
partial void IsSocketConnected(ref bool isConnected)
|
|
{
|
|
isConnected = (_socket != null && _socket.Connected);
|
|
#if FEATURE_SOCKET_POLL
|
|
if (isConnected)
|
|
{
|
|
// synchronize this to ensure thread B does not reset the wait handle before
|
|
// thread A was able to check whether "bytes read from socket" signal was
|
|
// actually received
|
|
lock (_socketReadLock)
|
|
{
|
|
DiagnosticAbstraction.Log(string.Format("[{0}] {1} Checking socket", ToHex(SessionId), DateTime.Now.Ticks));
|
|
|
|
// reset waithandle, as we're only interested in reads that take
|
|
// place between Poll and the Available check
|
|
_bytesReadFromSocket.Reset();
|
|
var connectionClosedOrDataAvailable = _socket.Poll(100, SelectMode.SelectRead);
|
|
isConnected = !(connectionClosedOrDataAvailable && _socket.Available == 0);
|
|
if (!isConnected)
|
|
{
|
|
// the race condition is between the Socket.Poll call and
|
|
// Socket.Available, but the event handler - where we signal that
|
|
// bytes have been received from the socket - is sometimes invoked
|
|
// shortly after
|
|
isConnected = _bytesReadFromSocket.WaitOne(500);
|
|
}
|
|
|
|
DiagnosticAbstraction.Log(string.Format("[{0}] {1} Checked socket", ToHex(SessionId), DateTime.Now.Ticks));
|
|
}
|
|
}
|
|
#endif // FEATURE_SOCKET_POLL
|
|
}
|
|
}
|
|
}
|