2017-02-18 122 views
0

我正在寻找像Git和Rsync这样的程序复制通过SSH连接进行通信和传输数据的方式,但是使用Python。据我所知,这些程序fork和exec一个SSH命令,用于在服务器端启动一个进程,并通过父进程与分叉的子进程的STDIN和STDOUT进行通信来实现通信。Python - 通过套接字与子进程进行通信

在CI中,通过创建一个Unix套接字对(s0,s1),分叉进程,将子进程的分支进程的标准输入/标准输出指向s1,然后读取和写入套接字s0在父进程上。

我想在Python3中做同样的事情。作为概念验证,这里是我的实现是发送命令的插座,并接收来自同一插座输出的玩具远程shell的:

import subprocess 
import socket 


ssh_cmd = 'ssh -q -T [email protected]' 
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'被阻塞,所以这并不好。

  1. 这是网络编程中一个明智/常见的事情吗?
  2. 使用不会死锁的Python3标准库来做这件事的更习惯方法是什么?
+0

你想要做的是相当明智的。你可能想研究'pexpect'模块。它是为这种事情量身定做的:驾驶交互式应用程序。请参阅https://pexpect.readthedocs.io/en/stable/overview.html以取得良好开端。它还有能力在你期待响应但没有响应的情况下在你的交互中放置超时。 –

回答

1

我连接的web浏览器到stdin并在SSH连接的远程端运行的命令的stdout。这听起来像是你正在尝试做类似的事情:将套接字连接到运行在远程计算机上的程序,以便客户端可以连接到主机/端口并使用所述远程程序。

您需要做的第一件事就是建立一个SSH连接。我在子进程中使用外部程序ssh(取决于操作系统的sshplink)。你可以使用类subprocess.Popen来做到这一点。子进程启动(外部)ssh应用程序以连接到远程主机并运行所需的命令。然后在那里听着它的“标准输入”并愿意回复它的“标准输出”。

(你可能使用Python SSH实现如的paramiko这里)

在一个非常高的水平(其中remote_command是在远端运行的东西):

import subprocess 
command = ['ssh', '[email protected]', 'remote_command'] 

sproc = subprocess.Popen(command, shell=False, 
         stdin = subprocess.PIPE, 
         stdout = subprocess.PIPE, 
         stderr = subprocess.PIPE) 

这留给你一个子进程,你可以通过它的stdin,stdoutstderr进行交互。

获得此位先工作。

接下来你需要的是绑定一个套接字,监听并接受一个连接。然后从客户端读取并写入服务器。我写了一个轻量级的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) 

您应该套接字读了起来:thisthis是有用的在这里。

然后,您可以使用此连接到您的remote_command

Socat(('localhost', 8080), sproc) 

有些失位

你会注意到socat类需要在stream(不像我的例子),其有writereadline方法。我实际上将SSH连接封装在一个班级中,但我会将其作为练习给读者!

(基本上包含sproc并提供writereadline方法class Stream - 有效sproc.stdin.write()sproc.stdout.readline() - !你的想法)

此外,您还需要一个协议,允许remote_command信号传输的结束。我使用ASCII EOT字符,我的远程程序在响应结束时发送。 socat使用它来关闭客户端连接并接受新连接。根据您的使用情况,您需要提出适用于您的协议。

最后

上述所有可以改进。以上是我第一次削减,因为它很容易看到内部工作。我已经将它扩展为在线程中运行接受循环,并优雅地处理断开连接等。

但希望以上是合理的出发点。

相关问题