C# SSH.NET库使用方法 C#如何通过SSH执行远程命令和SFTP

来源:这里教程网 时间:2026-02-21 17:41:27 作者:

SSH.NET 连接远程主机失败的常见原因

连接直接抛出

SocketException
或超时,大概率不是代码写错,而是底层网络或认证环节卡住了。先确认:目标主机是否开启 SSH 服务(默认端口 22)、防火墙是否放行、IP 和端口是否填对——这些比查
SshClient
构造函数参数更优先。

认证方式必须匹配服务端配置:

PasswordConnectionInfo
适用于密码登录;若用密钥,
PrivateKeys
列表里传入的
RsaKey
PrivateKeyFile
必须是 OpenSSH 格式(PEM),且私钥不能有密码保护(否则要额外处理
KeyPassword
)。

容易踩的坑:

SshClient
构造时不传
ConnectionInfo
,而用无参构造后手动赋值,会导致连接时忽略认证信息
使用
PrivateKeyFile
时路径写错或文件被占用,异常信息里可能只显示“Authentication failed”,实际是读取私钥失败
.NET 6+ 默认禁用不安全的 SSH 协议算法(如 ssh-rsa),若服务端只支持老算法,需显式启用:
connectionInfo.Encryptions.Add("aes256-cbc"); connectionInfo.KeyExchangeAlgorithms.Add("diffie-hellman-group1-sha1");
(不推荐,仅临时绕过)

执行单条远程命令:用
RunCommand
还是
CreateShellStream

RunCommand
是最简方式,适合执行非交互式命令(如
ls -l /tmp
systemctl status nginx
)。它自动处理 stdin/stdout/stderr 流,并返回
SshCommand
对象,其中
Result
是标准输出内容,
ExitStatus
是退出码。

但注意:

RunCommand
启动的是非登录 shell,不会加载用户 profile,PATH 可能不全。如果命令依赖别名、自定义环境变量或需要 cd 到特定目录,得显式写全路径或组合成一行:

var cmd = client.RunCommand("cd /var/log && tail -n 10 app.log");
Console.WriteLine(cmd.Result);

需要交互式会话(比如运行

sudo
并等待密码提示、启动
vim
等)就不能用
RunCommand
,得用
CreateShellStream
+ 手动读写流,复杂度陡增,一般应避免。

SFTP 文件上传/下载:必须先建立
SftpClient
实例

SshClient
SftpClient
是两个独立对象,即使共用同一套
ConnectionInfo
,也必须分别调用
Connect()
。SFTP 不是 SSH 的子通道自动复用,而是基于 SSH 连接之上的另一个协议会话。

实操要点:

SftpClient
的构造参数和
SshClient
一样,建议复用同一个
ConnectionInfo
实例,避免凭据重复管理
上传用
UploadFile
(本地
FileStream
→ 远程路径),下载用
DownloadFile
(远程路径 → 本地
FileStream
);不要直接用
WriteAllBytes
写入字节数组,大文件会爆内存
远程路径必须是绝对路径(如
/home/user/data.zip
),相对路径行为不可靠;上传前确保目标目录存在,
SftpClient
不自动创建父目录
下载时若远程文件正在被写入(如日志轮转中),可能读到不完整内容——没有原子性保证,业务层需自行加锁或校验

连接复用与资源释放:别让
Dispose
成为盲区

SshClient
SftpClient
都实现了
IDisposable
,且内部持有 TCP 连接和加密上下文。不调用
Dispose()
或不用
using
块,会导致连接泄漏,短时间内反复新建连接可能触发服务端限流或本地端口耗尽。

典型安全写法:

using (var client = new SshClient(connectionInfo))
{
    client.Connect();
    var cmd = client.RunCommand("uptime");
    Console.WriteLine(cmd.Result);
} // 自动 Disconnect + Dispose

如果需要在多个操作间复用连接(比如先执行命令再传文件),务必确保所有客户端都共享同一

ConnectionInfo
,且只在一个地方负责
Connect()
Disconnect()
。混用
using
和手动生命周期管理极易出错。

真正容易被忽略的是:SFTP 操作失败(如权限拒绝)后,

SftpClient
的状态可能已损坏,继续调用其他方法会抛出奇怪异常,此时应丢弃当前实例、重建并重连。

相关推荐