是否有人能在twisted的FTP服务器中实现REST命令?我目前的尝试是:
from twisted.protocols import ftp
from twisted.internet import defer
class MyFTP(ftp.FTP):
def ftp_REST(self, pos):
try:
pos = int(pos)
except ValueError:
return defer.fail(CmdSyntaxError('Bad argument for REST'))
def all_ok(result):
return ftp.REQ_FILE_ACTN_PENDING_FURTHER_INFO # 350
return self.shell.restart(pos).addCallback(all_ok)
class MyShell(ftp.FTPShell):
def __init__(self, host, auth):
self.position = 0
...
def restart(self, pos):
self.position = pos
print "Restarting at %s"%pos
return defer.succeed(pos)当客户端发送REST命令时,在脚本输出中看到这个命令需要几秒钟时间:
Traceback (most recent call last):
Failure: twisted.protocols.ftp.PortConnectionError: DTPFactory timeout
Restarting at <pos>我做错了什么?在我看来,应该立即响应REST命令,为什么套接字超时?
更新:
在启用Jean-Paul Calderone建议的日志记录之后,在DTP连接因缺乏连接而超时之前,REST命令甚至都没有到达我的FTP类(时间戳简化为MM:SS以表示简洁):
09:53 [TrafficLoggingProtocol,1,127.0.0.1] cleanupDTP
09:53 [TrafficLoggingProtocol,1,127.0.0.1] <<class 'twisted.internet.tcp.Port'> of twisted.protocols.ftp.DTPFactory on 37298>
09:53 [TrafficLoggingProtocol,1,127.0.0.1] dtpFactory.stopFactory
09:53 [-] (Port 37298 Closed)
09:53 [-] Stopping factory <twisted.protocols.ftp.DTPFactory instance at 0x8a792ec>
09:53 [-] dtpFactory.stopFactory
10:31 [-] timed out waiting for DTP connection
10:31 [-] Unexpected FTP error
10:31 [-] Unhandled Error
Traceback (most recent call last):
Failure: twisted.protocols.ftp.PortConnectionError: DTPFactory timeout
10:31 [TrafficLoggingProtocol,2,127.0.0.1] Restarting at 1024ftp_PASV命令返回DTPFactory.deferred,它被描述为“在连接实例时触发的延迟”。RETR命令很好(否则ftp.FTP将毫无价值)。
这使我相信,这里有某种阻塞操作,在DTP连接完成之前不会发生任何其他事情;然后,只有这样,我们才能接受进一步的命令。不幸的是,它看起来像是一些(全部?)客户端(特别是,我正在用FileZilla进行测试)在尝试恢复下载时,在连接之前发送REST命令。
发布于 2011-01-19 15:42:08
在深入挖掘源代码和篡改想法之后,我确定了这一解决方案:
class MyFTP(ftp.FTP):
dtpTimeout = 30
def ftp_PASV(self):
# FTP.lineReceived calls pauseProducing(), and doesn't allow
# resuming until the Deferred that the called function returns
# is called or errored. If the client sends a REST command
# after PASV, they will not connect to our DTP connection
# (and fire our Deferred) until they receive a response.
# Therefore, we will turn on producing again before returning
# our DTP's deferred response, allowing the REST to come
# through, our response to the REST to go out, the client to
# connect, and everyone to be happy.
resumer = reactor.callLater(0.25, self.resumeProducing)
def cancel_resume(_):
if not resumer.called:
resumer.cancel()
return _
return ftp.FTP.ftp_PASV(self).addBoth(cancel_resume)
def ftp_REST(self, pos):
# Of course, allowing a REST command to come in does us no
# good if we can't handle it.
try:
pos = int(pos)
except ValueError:
return defer.fail(CmdSyntaxError('Bad argument for REST'))
def all_ok(result):
return ftp.REQ_FILE_ACTN_PENDING_FURTHER_INFO
return self.shell.restart(pos).addCallback(all_ok)
class MyFTPShell(ftp.FTPShell):
def __init__(self, host, auth):
self.position = 0
def restart(self, pos):
self.position = pos
return defer.succeed(pos)callLater方法有时可能会变小,但它在大多数情况下都能工作。显然,这是你自己承担的风险。
发布于 2010-11-29 17:15:18
验证客户端的行为是否与您预期的一样。使用tcpdump或wireshark捕获所有相关通信量是一种很好的方法,尽管您也可以通过多种方式(例如,通过使用工厂包装器twisted.protocols.policies.TrafficLoggingFactory)在基于Twisted的FTP服务器中启用日志记录。
从超时错误到“重新启动.”日志消息,我想猜是客户端发送了一个RETR‘第一个’,然后是一个REST。RETR超时是因为客户端直到收到对其余部分的响应后才尝试连接到数据通道,而Twisted服务器甚至在客户端连接到数据通道(并下载整个文件)之后才会处理其余部分。解决这个问题可能需要改变ftp.FTP从客户端处理命令的方式,这样就可以正确地解释REST (或者您正在使用的FTP客户端仅仅是错误的,根据我可以找到的协议文档,RETR应该遵循REST,而不是相反)。
不过,这只是猜测,您应该查看流量捕获来确认或拒绝它。
https://stackoverflow.com/questions/4305127
复制相似问题