使用Renci.SshNet断开SshClient时触发System.ObjectDisposedException异常
问题描述
使用Renci.SshNet执行SSH命令的代码如下:
public int ExecuteShellCommand(string a_command, bool log = false) { int res = 0; try { using (var sshclient = new SshClient(m_connNfo)) { sshclient.Connect(); using (var cmd = sshclient.CreateCommand(a_command + " >&2")) { var resultExe = cmd.Execute(); if (log) { Console.Write(resultExe); var extended = cmd.ExtendedOutputStream; var reader = new StreamReader(extended); Console.Write(reader.ReadToEnd()); } res = cmd.ExitStatus; } } } catch (Exception) { return -1; } return res; }
其中连接信息m_connNfo的创建代码:
m_connNfo = new ConnectionInfo(ConfigurationManager.AppSettings[IP], 22, User, new AuthenticationMethod[]{ // Pasword based Authentication new PasswordAuthenticationMethod(User,ApiInstance.GetPass()) });
该方法可正常执行并实现预期功能,但在try块结束(using块释放SshClient)时,或提前调用sshclient.Disconnect();后,终端会抛出System.ObjectDisposedException in System.dll异常,需分析异常产生原因。
异常原因分析
- 流资源未正确释放:读取
ExtendedOutputStream时创建的StreamReader未用using包裹,导致该对象持续持有对输出流的引用。当SshClient释放底层连接时,会尝试关闭所有关联流,此时StreamReader仍引用已被释放的流对象,触发ObjectDisposedException。 - 命令重定向引发流处理冲突:命令后追加的
>&2将标准输出重定向到标准错误流,即便代码读取了两个流,仍可能存在流数据未完全读取的情况。SshClient释放时会清理未处理完的流资源,此时访问已处于释放状态的流会引发异常。 - 资源释放顺序的潜在冲突:虽然using块会按顺序释放Command和SshClient,但如果Command的流资源未完全清理完成,SshClient关闭连接时会强制终止相关流操作,导致访问已释放对象的异常。
解决建议
- 给
StreamReader添加using块,确保流资源被正确释放:
if (log) { Console.Write(resultExe); using (var reader = new StreamReader(cmd.ExtendedOutputStream)) { Console.Write(reader.ReadToEnd()); } }
- 移除命令中的
>&2重定向,分别读取标准输出和标准错误流,避免流混淆:
using (var cmd = sshclient.CreateCommand(a_command)) { var resultExe = cmd.Execute(); if (log) { Console.Write(resultExe); using (var reader = new StreamReader(cmd.ExtendedOutputStream)) { Console.Write(reader.ReadToEnd()); } } res = cmd.ExitStatus; }
- 在释放Command前,确保所有流操作完成,可手动调用
cmd.Dispose()确保资源清理完毕。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

