Paramiko 服务器:向客户端发出 stdout 已关闭的信号

Paramiko Server: Signalling the client that stdout is closed

尝试在 paramiko 中实现测试服务器,而无需修改客户端进行测试 , 我偶然发现了如何关闭 stdout 流的问题,使“stdout.read()”不会永远挂起而不会在客户端太低级别。到目前为止,我已经能够通过以下方式传达完成的命令(简单文本输出到标准输出)执行:

class FakeCluster(paramiko.server.ServerInterface):
    def check_channel_exec_request(self,channel,command):
        writemessage = channel.makefile("w")
        writemessage.write("SOME COMMAND SUBMITTED")
        writemessage.channel.send_exit_status(0)
        return True

但是我还没有找到避开中间两行的方法

_,stdout,_ = ssh.exec_command("<FILEPATH>")
    stdout.channel.recv_exit_status()
    stdout.channel.close()
    print(stdout.read())

这已经是一个很好的解决方法,不必直接调用 channel.exec_command(发现 here)。 不关闭 stdout 流,我的输出将不会打印,并且服务器上的底层传输也永远保持活动状态。

使用 stdout.channel.close() 关闭频道并没有真正的效果,而是使用 os.close(writemessage.fileno())(差异解释 here) does not work because the paramiko.channel.ChannelFile object used for the I/O streams "has no attribute 'fileno'". (Detailed explanation found 。)

此外,直接在服务器端关闭通道会为客户端抛出 SSHException..

建议的解决方案 here 总是修改客户端,但我从在实际服务器上使用我的客户端脚本知道,没有这些额外的行一定是可能的!

check_channel_exec_request 中,发送退出状态后关闭服务器端的通道,根据协议规范,该规范声明通道在命令执行的整个生命周期内处于活动状态,并在之后关闭。

这导致 channel.eof() 在客户端成为 True,表示命令已完成并且从通道读取不再挂起。

def check_channel_exec_request(self,channel,command):
    writemessage = channel.makefile("w")
    writemessage.write("SOME COMMAND SUBMITTED")
    writemessage.channel.send_exit_status(0)
    channel.close()
    return True

看到这个 embedded server for integration testing based on paramiko that has been around for some years for reference - it implements exec requests among others. Speaking from experience, I would recommend instead using an embedded OpenSSH based server, an example of which can also be found on the same repository。 Paramiko 代码并不是特别没有错误。

我遇到过一个与此类似的问题。我们的问题是我们一退出就关闭了整个 session。显然我们的客户(libssh2)不喜欢那样。因此,我们每次关闭一个频道时都会继续尝试接受一个新频道,直到 transport.is_active()False