4

我有一个线程化的 FTP 脚本。当数据套接字接收数据时,线程循环将 NOOP 命令发送到控制套接字,以在大传输期间保持控制连接处于活动状态。

我被阻止使用该FTP.retrbinary()命令,因为如果我想保持控制连接处于活动状态,我必须将数据和控制套接字分开,而这retrbinary不起作用。

下面的代码:

def downloadFile(filename, folder):
    myhost = 'HOST'
    myuser = 'USER'
    passw = 'PASS'
    #login
    ftp = FTP(myhost,myuser,passw)

    ftp.set_debuglevel(2)
    ftp.voidcmd('TYPE I')
    sock = ftp.transfercmd('RETR ' + filename)
    def background():
        f = open(folder + filename, 'wb')
        while True:
            block = sock.recv(1024*1024)
            if not block:
                break
            f.write(block)
        sock.close()
    t = threading.Thread(target=background)
    t.start()
    while t.is_alive():
        t.join(120)
        ftp.voidcmd('NOOP')
    ftp.quit();


我的问题FTP.transfercmd("RETR " + filename)默认为 ASCII 传输和我传输视频,所以它必须是二进制的(因此ftp.voidcmd('TYPE I)调用强制二进制模式)。

如果我不调用NOOPftp.voidcmd('TYPE I)命令发送成功,输出如下:

*cmd* 'NOOP'
*put* 'NOOP\r\n'
*get* '200 NOOP: data transfer in progress\n'
*resp* '200 NOOP: data transfer in progress'
*cmd* 'NOOP'
*put* 'NOOP\r\n'
*get* '200 NOOP: data transfer in progress\n'
*resp* '200 NOOP: data transfer in progress'
*cmd* 'NOOP'
*put* 'NOOP\r\n'
*get* '200 NOOP: data transfer in progress\n'
*resp* '200 NOOP: data transfer in progress'

等等,但文件是 ASCII 格式,因此已损坏。如果我调用, NOOPftp.voidcmd('TYPE I)命令只发送一次,并且控制套接字在传输完成之前不会响应。如果文件很大,则控制套接字会超时,就好像从未发送过 NOOP...

很奇怪,但我相信它很简单。似乎它transfercmd()没有像预期的那样拆分控制和数据套接字......因此 ftp var 没有与数据流分离......或其他东西。奇怪的。

提前感谢您提供的任何建议。

4

1 回答 1

3

tcpdump确认服务器仅在服务器发送226 Transfer complete.整个文件后发送。

我怀疑这是 FTP 规范的一部分。

事实上,看看retrbinary代码ftplib.py

    self.voidcmd('TYPE I')
    conn = self.transfercmd(cmd, rest)
    while 1:
        data = conn.recv(blocksize)
        if not data:
            break
        callback(data)
    conn.close()
    return self.voidresp()

最后一行期望仅在传输完成后才能获得传输结果(服务器已知)。

实际上,您的代码似乎缺少voidresp()一点。

我对 ftp 不是很熟悉,从我所看到的后台下载器来看,lftp实际上为每个并行下载打开了新的控制连接。

如果您的文件真的很大,您有一个有效的担忧。

FTP有很多扩展,可能有一些你想要的东西。

或者,您可以像这样进行循环:

pos = 0
while not full file:
    command REST
    download for a while in separate thread
    command ABRT
    wait for separate thread to abort
    pos += length of downloaded chunk
于 2013-11-21T17:22:49.633 回答