-
-
Notifications
You must be signed in to change notification settings - Fork 947
PipeStream returns 0 instead of blocking when running a command with intermittent output #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I kinda resolved this with this patch:
Though I imagine you may have a better approach in mind. If you would like, I could create a pull request with this patch... Just let me know. |
… intermittent output sshnet#12
@lucastheisen I know this is older and I'm sure you've since moved on from this. But, I saw your StackOverflow question and provided an answer. I've replicated it here: Another problem with public class SshCommandStreamReader : IDisposable
{
private readonly Stream stream;
private readonly MemoryStream intermediateStream;
private readonly StreamReader reader;
public SshCommandOutputReader(Stream stream)
{
this.stream = stream;
this.intermediateStream = new MemoryStream();
this.reader = new StreamReader(intermediateStream, Encoding.UTF8);
}
private int FlushToIntermediateStream()
{
var length = stream.Length;
if (length == 0)
{
return 0;
}
// IMPORTANT: Do NOT read with a count higher than the stream length (which is typical of reading
// from streams). The streams for SshCommand are implemented by PipeStream (an internal class to
// SSH.NET). Reading more than the current length causes it to *block* until data is available.
// If the stream is flushed when reading, it does not block. It is not reliable to flush and then
// read because there is a possible race condition where a write might occur between flushing and
// reading (writing resets the flag that it was flushed). The only reliable solution to prevent
// blocking when reading is to always read the current length rather than an arbitrary buffer size.
var intermediateOutputBuffer = new byte[length];
var bytesRead = stream.Read(intermediateOutputBuffer, 0, intermediateOutputBuffer.Length);
intermediateStream.Write(intermediateOutputBuffer, 0, bytesRead);
return bytesRead;
}
public string Read()
{
var bytesFlushed = FlushToIntermediateStream();
// Allow reading the newly flushed bytes.
intermediateStream.Position -= bytesFlushed;
// Minor optimization since this may be called in a tight loop.
if (intermediateStream.Position == intermediateStream.Length)
{
return null;
}
else
{
var result = reader.ReadToEnd();
return result;
}
}
public void Dispose()
{
reader.Dispose();
intermediateStream.Dispose();
}
} And then use it: using (var command = client.CreateCommand("your command text"))
{
var cmdAsyncResult = command.BeginExecute();
using (var standardOutputReader = new SshCommandStreamReader(command.OutputStream))
{
while (!cmdAsyncResult.IsCompleted)
{
var result = standardOutputReader.Read();
if (!String.IsNullOrEmpty(result))
{
Console.Write(result);
}
// Or what ever mechanism you'd like to use to prevent CPU thrashing.
Thread.Sleep(1);
}
// This must be done *after* the loop and *before* EndExecute() so that any extra output
// is captured (e.g. the loop never ran because the command was so fast).
var resultFinal = standardOutputReader.Read();
if (!String.IsNullOrEmpty(resultFinal))
{
Console.Write(resultFinal);
}
}
command.EndExecute(cmdAsyncResult);
} You should be able to modify this sample to read from standard error (via the |
For details, you can see this issue on StackOverflow.
The text was updated successfully, but these errors were encountered: