我有一个线程化的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)
调用强制二进制模式)。
如果我 DONT 调用ftp.voidcmd('TYPE I)
,NOOP命令会成功发送,输出如下:
*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格式,因此已损坏。如果我 DO 调用ftp.voidcmd('TYPE I)
,则NOOP命令仅发送一次,并且控制套接字不响应,直到传输完成。如果文件很大,控制套接字就会超时,就像从未发送过NOOP一样......
很奇怪,但我确信它很简单。似乎transfercmd()
没有按照它应该分割控件和数据套接字......因此ftp var不会与数据流分离......或者其他东西。奇怪。
提前感谢您提出的任何建议。
答案 0 :(得分:2)
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