我希望复制像Git和Rsync这样的程序通过SSH连接进行通信和传输数据的方式,但是在Python中。据我所知,这些程序分叉并执行SSH命令,该命令在服务器端启动进程,并且通过父进程与分叉子进程的STDIN和STDOUT进行通信来实现通信。
在CI中已经通过创建一个Unix套接字对(s0,s1),分叉进程,将分叉进程的stdin / stdout指向子进程上的s1,然后读取和写入套接字s0来完成在父进程上。
我想在Python3中做同样的事情。作为概念验证,这里是我的玩具远程shell的实现,它向套接字发送命令并从同一套接字接收输出:
import subprocess
import socket
ssh_cmd = 'ssh -q -T ubuntu@xxxxxxxx'
s_local, s_remote = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
ssh = subprocess.Popen(ssh_cmd, shell=True,
stdout=s_remote,
stdin=s_remote,
close_fds=True)
s_remote.close()
s_local.send('ls -l\n'.encode())
print(s_local.recv(1024).decode())
s_local.send('uname -a\n'.encode())
print(s_local.recv(1024).decode())
s_local.close()
ssh.kill()
这种作品。但是如果我在套接字上发送'true \ n',因为'recv'阻塞所以这不是很好。我可以使它死锁。
答案 0 :(得分:2)
我将Web浏览器连接到SSH连接远程端上运行的命令的stdin
和stdout
。听起来你正在尝试做类似的事情:将套接字连接到远程机器上运行的程序,以便客户端可以连接到主机/端口并使用所述远程程序。
您需要做的第一件事是建立SSH连接。我在子进程中使用外部ssh
程序(ssh
或plink
,具体取决于操作系统)。你可以使用类subprocess.Popen
来做到这一点。子进程启动(外部)ssh
应用程序以连接到远程主机并运行所需的命令。然后坐在那里听'stdin'并愿意回复'stdout'。
(你可以在这里使用像Paramiko这样的Python SSH实现)
处于非常高的水平(其中remote_command
是在远端运行的东西):
import subprocess
command = ['ssh', 'user@host', 'remote_command']
sproc = subprocess.Popen(command, shell=False,
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE)
这为您提供了一个子流程,您可以通过其stdin
,stdout
和stderr
进行交互。
先让这一点工作。
您需要的下一件事是绑定套接字,监听并接受连接。然后从客户端读取并写入服务器。我写了一个轻量级Socat
(灵感来自Socat)类:
import socket
class Socat(object):
EOT = '\004\r\n'
def __init__(self, binding, stream, eot = EOT):
self.stream = stream
self.eot = eot
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind(binding)
self.socket.listen(5)
while True:
print "waiting for connection"
(clientsocket, address) = self.socket.accept()
print "connection from %s:%s" % address
data = clientsocket.recv(1024)
self.stream.write(data)
while data:
data = self.stream.readline()
if data:
print "read %s bytes" % len(data)
if data == self.eot:
print "read termination signature"
clientsocket.close()
data = None
else:
clientsocket.send(data)
然后,您可以使用此功能连接到remote_command
:
Socat( ('localhost', 8080), sproc)
缺少一些内容
您会注意到socat
课程采用stream
(与我的示例不同),其中包含write
和readline
方法。我实际上在一个类中封装了SSH连接,但我将把它作为读者的练习!
(基本上class Stream
包含sproc
并提供write
和readline
方法 - 有效sproc.stdin.write()
和sproc.stdout.readline()
- 你得到了想法!)
此外,您需要一个协议来允许remote_command
发出传输结束的信号。我使用ASCII EOT
字符,我的远程程序在响应结束时发送它。 socat
使用它来关闭客户端连接并接受新连接。根据您的使用情况,您需要提出适合您的协议。
<强>最后强>
以上所有都可以改进。以上是我的第一次切割,因为很容易看到内部工作原理。我已经扩展它以在一个线程中运行accept循环并优雅地处理断开连接等等。
但希望上面的内容是一个合理的起点。