同步读挂起

Synchronous read hangs

在过去的几天里,我试图让以下代码同步工作:

Process process = new Process();
process.StartInfo.FileName = @"c:\plink.exe";
process.StartInfo.Arguments = "-t -ssh -l username -pw password 1.1.1.1";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.Start();

StreamWriter sw = process.StandardOutput;
StreamReader sr = process.StandardInput;

string output="";
sw.WriteLine ("configure terminal");
sw.Flush();
while (sr.Peek() > -1) 
{ 
    output += (char) sr.Read(); 
}

if (output.Contains("(config)")
{
    output="";
    sw.WriteLine ("interface TenGig1/0/1");
    sw.Flush();
    while (!sr.EndOfStream) 
    { 
        output+= (char) sr.Read(); 
    }
    if (output.Contains("(config-if")) 
    { 
        sw.WriteLine ("shutdown"); 
        sw.Flush();
    }
}

问题 - 读取挂起!

无处不在VisualStudio/IIS8愿。每个 Read 操作都容易受到影响,而且更常见的是,上面代码中的每个 Read 都会挂起,而不是成功读取。请注意,在同一个项目中,我有 多个 异步重定向,它们非常有用。然而,在这里,我被迫同步进行——因为我阅读并交互式地响应我红色的内容。欢迎任何建议。

编辑 1 删除了我在寻求社区帮助之前执行的调试步骤。 澄清第一个和第二个 while 的测试条件不同,因为流似乎以其他方式出现在 EndOfStream。

我有一个 IIS 应用程序,其功能几乎相同。我的代码是:

var a_proc = new Process
{
    StartInfo = new ProcessStartInfo
    {

        FileName = @"C:\Test.exe",
        Arguments = $"-path {a_file}",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    }
};

a_proc.Start();

while (!a_proc.StandardOutput.EndOfStream)
{
    string line = a_proc.StandardOutput.ReadLine().Trim();
    a_result += line + '\n';
}

此代码将启动 exe 并读取任何控制台输出,直到控制台应用程序关闭。它读取同步,这意味着它将在此行 string line = a_proc.StandardOutput.ReadLine().Trim(); 等待,直到控制台写入新行。这可以看作是 "hangs" 但实际上它只是等待读取新行。

我想问题出在您的行 while (!sr.EndOfStream) 代码等待控制台关闭。但正如我所见,你想在那之后写 /"shutdown" 控制台。这对我来说毫无意义。尝试使用 StandardOutput.ReadLine() 读取该行,然后检查它是否包含 "(config-if".

希望对您有所帮助!

经过几天的研究,我想我找到了一个答案 - 我决定在这里分享这个答案,因为我不是唯一一个 运行 参与其中的人。也许我的努力,会对某人有用。首先,我会 post 一个简短的回答,然后,我会 post 我的研究和解释。由于我不认为自己是 C# 或 Dot Net 大师,我很高兴社区就此进行协作,更正和改进此答案。这样我们就能让 StackOverfllow 对每个人都更好,不是吗? :)

简答:

你无能为力。这种死锁似乎是同步读取的实现方式所固有的(我使用的是 .Net 4.0 Framework),因此如果可以的话 - 首先要避免 运行 陷入麻烦,而更喜欢异步读取。由于您的代码可能 运行 在您的 PC(=开发环境)上,但无法部署到 IIS,就像它对我所做的那样。可能 您可以使用的唯一解决方法 是当您的预期输出(=您确定(!)有一个输出,等待变为红色)以您可以识别和识别的确定模式结束时停止阅读.

长答案:

当您尝试从标准输出中读取内容时,问题就会出现,而那里没有任何东西在等您。

当 (a) 首先没有输出,或者 (b) 您刚刚读完那里的内容并“前进到下一个”(不存在的)字符或字符串时,可能会出现这种情况。虽然 (b) 更危险恕我直言,因为当你期望没有输出时很容易不读,但更难停止,当没有条件让你知道什么时候(至少,我无法通过调试我的MSDN 中的代码和记录 - 对我的问题的沉默是相同的另一个证据)。

乍一看,您可以使用 Peek() 方法,该方法承诺“Returns 下一个可用字符但不使用它”,使用 Peek 时,您可以轻松地在网络上找到示例() 似乎可以解决问题。不幸的是,MSDN 还说“如果没有要读取的字符或 如果流不支持搜索 ,则为 -1”。猜猜是什么,至少在我的情况下——标准输出不支持查找。因此 Peek() 相当不可用。它确实工作了几次(也许有更深入了解的人可以解释为什么),但我怀疑在这种情况下我没有得到完整的输出。

现在,我想重复我使用过的方法及其结果,以便您可以从我的研究中学习并了解您的代码的期望:

  • StreamReader.Read() 在 While(!StreamReader.EndOfStream) 内 - 挂起 执行时.
  • StreamReader.Read() within(StreamReader.Peek() > -1) - 跳过循环.
  • StreamReader.ReadLine() - 执行时挂起
  • StreamReader.ReadToEnd() - 执行时挂起
  • StreamReader.BaseStream.Length - 运行 时的异常:流不支持搜索
  • StreamReader.Read() / StreamReader.BaseStream.ReadByte() - 一个特例,据此我得出了我的结论 - 两者都读得很好,直到准确读取当前字符之后的下一个字符缓冲区结束。比挂起。

在我看来,当前的实施或 Process.StandardOutput 缺乏任何识别我们何时变红的方法。这就是为什么在我看来,除了 Read() 和 ReadByte() 之外的所有方法都会挂起。如果您跨过当前的溪流边缘,那两个也会挂起。就我而言,我能够转移到 SSH.Net(我要感谢那个出色项目的贡献者)。顺便说一句,它永远不会挂在 sshReader.ReadToEnd() :)

PPS:我的开发环境适合那些想知道的人:VisualStudio 2010 Ultimate on Windows 7 64bit 和 IIS 8(全新安装)与 Framework 4。