【问题标题】:How to timeout Paramiko sftp.put() with signal module or other in Python?如何使用 Python 中的信号模块或其他模块使 Paramiko sftp.put() 超时?
【发布时间】:2019-11-13 18:34:35
【问题描述】:

我想让函数sftp.put() 超时,我尝试过使用信号模块,但如果上传时间超过 10 秒,脚本不会死机。 我用它通过 ssh (paramiko) 传输文件。

[...]
def handler(signum, frame):
        print 'Signal handler called with signal', signum
        raise IOError("Couldn't upload the fileeeeeeeeeeee!!!!")

[...]
raspi = paramiko.SSHClient()
raspi.set_missing_host_key_policy(paramiko.AutoAddPolicy())
raspi.connect(ip , username= "", password= "" , timeout=10)
sftp = raspi.open_sftp()        

[...]   
signal.signal(signal.SIGALRM, handler)
signal.alarm(10)

sftp.put(source, destination , callback=None, confirm=True)  
    
signal.alarm(0)
raspi.close()

[...]

更新 1:

如果服务器停止响应一段时间,我想中止传输。实际上,我的 python 脚本检查(循环)文件夹中的任何文件,并将其发送到这个远程服务器。但是在这里的问题中,我想在服务器在传输过程中突然无法访问的情况下保留此功能(IP 服务器更改,不再有互联网,...)。但是当我模拟断开连接时,脚本仍然停留在这个函数 sftp.put...)

更新 2:

当服务器在传输过程中离线时,put() 似乎永远被阻塞。这行也会发生这种情况:

sftp.get_channel().settimeout(xx)

失去频道怎么办?

更新 3 和脚本目标

Ubuntu 18.04 和 paramiko 版本 2.6.0

你好, 为了遵循您的评论和问题,我必须提供有关我非常丑陋的脚本的更多详细信息,对此感到抱歉:)

实际上,我不想手动杀死一个线程并打开一个新线程。对于我的应用程序,我希望脚本完全自主运行,如果在此过程中出现问题,它仍然可以继续运行。为此,我使用 Python 异常处理。除了远程服务器在传输过程中关闭时,一切都按照我的意愿进行:脚本在 put() 函数中保持阻塞,我认为在循环中。 下面,脚本总共包含 3 个函数来超时,感谢您的帮助,但显然没有什么可以离开这个该死的 sftp.put()!你有什么新想法吗?

Import […]
[...]

def handler(signum, frame):
        print 'Signal handler called with signal', signum
        raise IOError("Couldn't upload the fileeeeeeeeeeee!!!!")

def check_time(size, file_size):
    global start_time
        if (time.time() - start_time) > 10:
            raise Exception

i = 0
while i == 0:

    try:

        
        time.sleep(1) # CPU break
        print ("go!")

        #collect ip server
        fichierIplist = open("/home/robert/Documents/iplist.txt", "r")
        file_lines = fichierIplist.readlines()
        fichierIplist.close()
        last_line = file_lines [len (file_lines)-1]
        lastKnowip = last_line

        data = glob.glob("/home/robert/Documents/data/*")   
        items = len(data) 

        if items != 0: 
        time.sleep(60) #anyway
            print("some Files!:)")
            raspi = paramiko.SSHClient()
            raspi.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            raspi.connect(lastKnowip, username= "", password= "" , timeout=10)

            for source in data: #Upload file by file    
                filename = os.path.basename(source) #
                destination = '/home/pi/Documents/pest/'+ filename #p

                sftp = raspi.open_sftp()

                signal.signal(signal.SIGALRM, handler)
                signal.alarm(10)

                sftp.get_channel().settimeout(10)
                start_time = time.time()

                sftp.put(source, destination, callback=check_time)

                sftp.close()    
                signal.alarm(0) 
            raspi.close()

        else:

            print("noFile!")

    except:
        pass

【问题讨论】:

    标签: python ssh sftp paramiko


    【解决方案1】:

    如果要超时,当服务器停止响应时:

    • 设置SSHClient.connecttimeout 参数(您已经这样做了),
    • 并按照@EOhm 的建议设置sftp.get_channel().settimeout

    如果您想在服务器响应但缓慢的情况下超时,请实现callback 参数以在一定时间后中止传输:

    start_time = time.time()
    
    def check_time(size, file_size):
        global start_time
        if (time.time() - start_time) > ...:
            raise Exception
    
    sftp.put(source, destination, callback=check_time)  
    

    这不会立即取消转移。为了优化传输性能,Paramiko 将写入请求排队到服务器。一旦您尝试取消传输,Paramiko 必须等待 SFTPFile.close() 中对这些请求的响应以清除队列。您可以通过使用SFTPClient.putfo() 并在取消传输时避免调用SFTPFile.close() 来解决这个问题。但是之后您将无法使用该连接。当然,您也可以不使用优化,然后您可以毫不拖延地取消传输。但这种做法违背了这一切的意义,不是吗?


    或者,您可以在单独的线程中运行传输,如果花费的时间太长,则终止该线程。丑陋但肯定的解决方案。

    【讨论】:

      【解决方案2】:

      请改用sftp.get_channel().settimeout(s)

      【讨论】:

      • 谢谢。但是如果我添加这一行,例如当服务器没有响应 5 秒时脚本不会停止。为什么?
      • 您是否在 Windows 上运行脚本?我认为由于某些原因它在那里不起作用.. Windows 上的信号等也非常不同,因此可能会非常相关。
      • 您好,我使用的是 Ubuntu 18.04。
      【解决方案3】:

      在尝试了很多事情并在您的帮助和建议下,我找到了我想要的可靠解决方案。我在一个单独的线程中执行 sftp.put,我的脚本做我想做的事。 非常感谢您的帮助

      现在如果服务器在传输过程中关闭,60 秒后,我的脚本继续使用:

      [...]
      import threading
      [...]
      th = threading.Thread(target=sftp.put, args=(source,destination))
      th.start()                  
      h.join(60)
      [...]
      

      【讨论】:

      • 好的,这当然是一种解决方法,但是 SFTP 会话和线程将继续运行直到超时。由于 Python 很遗憾除了关闭程序和线程守护进程之外没有办法杀死线程,显然 SFTP.put 不能被信号中断,这是您唯一的选择。遗憾的是,我没有时间在我的 Ubuntu 服务器上进一步调查通道超时方法。我很确定有更好更真实的方法,或者至少应该修复一个错误。
      猜你喜欢
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多