Skip to content

Conversation

@Rob-Hague
Copy link
Collaborator

The message loop currently sits in a call to Poll until the socket has data to read or it is closed. This is unnecessary - it can equally just sit in the call to Receive.

The call to Poll in Session.IsConnected is also unnecessary - we can instead just call Socket.Connected. This only returns the connection state as of the last operation, but we are always performing operations in the message loop (or else we are not connected), so it should work equally well while being cheaper.

Lastly, when shutting down the socket, shut down both sides rather than just the sending side (SocketShutdown.Both rather than SocketShutdown.Send) - at this point we do not care about reading anything else. This makes it (more) certain that we will break out of the Receive call in the message loop, as has been noted in #355 for whatever remaining issues still exist there.

The message loop currently sits in a call to Poll until the socket has data to read or
it is closed. This is unnecessary - it can equally just sit in the call to Receive.

The call to Poll in Session.IsConnected is also unnecessary - we can instead just call
Socket.Connected. This only returns the connection state as of the last operation, but
we are always performing operations in the message loop (or else we are not connected),
so it should work equally well while being cheaper.

Lastly, when shutting down the socket, shut down both sides rather than just the sending
side (SocketShutdown.Both rather than SocketShutdown.Send) - at this point we do not care
about reading anything else. This makes it (more) certain that we will break out of the
Receive call in the message loop, as has been noted in sshnet#355 for whatever remaining issues
still exist there.
/// Holds an object that is used to ensure only a single thread can read from
/// <see cref="_socket"/> at any given time.
/// </summary>
private readonly Lock _socketReadLock = new Lock();
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no longer needed

/// </summary>
private void SocketDisconnectAndDispose()
{
if (_socket != null)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a hot path, so simplify the locking and save some indentation


// any timeout while disconnecting can be caused by loss of connectivity
// altogether and should be ignored
if (exp is SocketException socketException && socketException.SocketErrorCode == SocketError.TimedOut)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dead code, we always wrap SocketException in SshConnection

Comment on lines -67 to -68
Assert.IsNull(connectionException.InnerException);
Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not always the case - it could contain a SocketException upon RST, in which case the message is more like "An existing connection was forcibly closed by the remote host."

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Rob-Hague
Copy link
Collaborator Author

Thanks

@Rob-Hague Rob-Hague merged commit c335ce2 into sshnet:develop Oct 1, 2025
4 checks passed
@Rob-Hague Rob-Hague deleted the socketpoll branch October 1, 2025 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants