我正在研究一個python腳本,每隔一段時間就通過建立的ssh隧道查詢一些遠程數據庫。我對paramiko圖書館非常熟悉,所以這是我選擇的路線。我寧願將它保留在完整的python中,以便我可以使用paramiko來處理關鍵問題,以及使用python來啓動,控制和關閉ssh隧道。Paramiko SSH隧道關閉問題
在這裏有一些關於這個話題的相關問題,但其中大多數在答案中似乎不完整。以下我的解決方案是迄今爲止發現的解決方案的混合。
現在的問題:我能夠很容易地創建第一個隧道(在一個單獨的線程中),並做我的DB/python的東西,但是當試圖關閉隧道localhost不會釋放本地端口我綁定到了。下面,我通過流程的每一步都包含了我的源代碼和相關的netstat數據。
#!/usr/bin/python
import select
import SocketServer
import sys
import paramiko
from threading import Thread
import time
class ForwardServer(SocketServer.ThreadingTCPServer):
daemon_threads = True
allow_reuse_address = True
class Handler (SocketServer.BaseRequestHandler):
def handle(self):
try:
chan = self.ssh_transport.open_channel('direct-tcpip', (self.chain_host, self.chain_port), self.request.getpeername())
except Exception, e:
print('Incoming request to %s:%d failed: %s' % (self.chain_host, self.chain_port, repr(e)))
return
if chan is None:
print('Incoming request to %s:%d was rejected by the SSH server.' % (self.chain_host, self.chain_port))
return
print('Connected! Tunnel open %r -> %r -> %r' % (self.request.getpeername(), chan.getpeername(), (self.chain_host, self.chain_port)))
while True:
r, w, x = select.select([self.request, chan], [], [])
if self.request in r:
data = self.request.recv(1024)
if len(data) == 0:
break
chan.send(data)
if chan in r:
data = chan.recv(1024)
if len(data) == 0:
break
self.request.send(data)
chan.close()
self.request.close()
print('Tunnel closed from %r' % (self.request.getpeername(),))
class DBTunnel():
def __init__(self,ip):
self.c = paramiko.SSHClient()
self.c.load_system_host_keys()
self.c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.c.connect(ip, username='someuser')
self.trans = self.c.get_transport()
def startTunnel(self):
class SubHandler(Handler):
chain_host = '127.0.0.1'
chain_port = 5432
ssh_transport = self.c.get_transport()
def ThreadTunnel():
global t
t = ForwardServer(('', 3333), SubHandler)
t.serve_forever()
Thread(target=ThreadTunnel).start()
def stopTunnel(self):
t.shutdown()
self.trans.close()
self.c.close()
雖然我最終會使用stopTunnel()類型的方法,我已經意識到,代碼是不完全正確的,但更多的努力得到正確的隧道關閉的實驗和測試我的結果。
當我首先調用創建DBTunnel對象並調用startTunnel(),netstat的產生以下:
tcp4 0 0 *.3333 *.* LISTEN
tcp4 0 0 MYIP.36316 REMOTE_HOST.22 ESTABLISHED
tcp4 0 0 127.0.0.1.5432 *.* LISTEN
一旦我請stopTunnel(),或者甚至刪除DBTunnel對象itself..I'm左與此相聯繫,直到我退出蟒蛇一起,我認爲是垃圾收集什麼照顧它:
tcp4 0 0 *.3333 *.* LISTEN
這將是很好找出爲什麼這個開放插座掛在數據庫連接對象的獨立,以及如何從內部正確關閉它我的腳本。如果我在完全退出python之前嘗試使用相同的本地端口綁定到不同IP的不同連接(time_wait不是問題),那麼我會得到臭名昭着的bind err 48地址。在此先感謝:)
上面的代碼演示了一個錯誤在原始l paramiko轉發示例代碼,這是self.request.getpeername()導致一個錯誤的文件描述符異常,因爲它是在請求關閉後調用的。修正是在這裏:https://github.com/paramiko/paramiko/pull/36 – 2011-12-29 23:06:58