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使用它來關閉客戶端連接並接受新連接。根據您的使用情況,您需要提出適用於您的協議。

最後

上述所有可以改進。以上是我第一次削減,因爲它很容易看到內部工作。我已經將它擴展爲在線程中運行接受循環,並優雅地處理斷開連接等。

但希望以上是合理的出發點。

相關問題