以下代码应该对FTP服务器进行身份验证,并检查是否允许写访问。只要使用单个主机,它就可以正常运行。
import ftplib
import queue
import threading
def check_ftp(options):
host = options.get('host')
user = options.get('user')
passwd = options.get('passwd')
port = options.get('port')
# Attempt to connect and authenticate
ftp = ftplib.FTP()
try:
ftp.connect(host, port, timeout=5)
ftp.login(user, passwd)
print('{}:{} - Login successful.'.format(host, port))
except(ftplib.error_perm, OSError) as e:
print('{}:{} - Unable to connect/auth: {}'.format(host, port, e))
return
# Attempt to write & get dir listing
is_writable = False
contents = []
try:
test_folder = 'test_folder'
ftp.mkd(test_folder)
print('{}:{} - FTP root is writable'.format(host, port))
is_writable = True
ftp.retrlines('LIST', contents.append)
ftp.rmd(test_folder)
except ftplib.error_perm as e:
ftp.retrlines('LIST', contents.append)
print('{}:{} - Not writable: {}'.format(host, port, e))
ftp.quit()
class ModuleRunner(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def run(self):
while True:
# gets the options from the queue
options = self.queue.get()
# run the module
check_ftp(options)
# Let queue know the job is one
self.queue.task_done()
def run(thread_count):
options =[
{'host': 'ftp.uconn.edu',
'user': 'anonymous',
'passwd': 'anonymous@test.com',
'port': 21},
{'host': 'speedtest.tele2.net',
'user': 'anonymous',
'passwd': 'anonymous@test.com',
'port': 21},
{'host': 'test.talia.net',
'user': 'anonymous',
'passwd': 'anonymous@test.com',
'port': 21},
]
# Fill queue
q = queue.Queue()
for opt in options:
q.put(opt)
# Create a thread pool
threads = thread_count
for i in range(threads):
t = ModuleRunner(q)
t.setDaemon(True)
t.start()
q.join()
if __name__ == '__main__':
run(1)
但是,如果添加了03个或更多主机(请参阅options变量),则每次脚本都会崩溃并挂起,即使使用了一个线程也是如此。错误:
ftp.uconn.edu:21 - Login successful.
ftp.uconn.edu:21 - Not writable: 550 test_folder: Permission denied
speedtest.tele2.net:21 - Login successful.
speedtest.tele2.net:21 - Not writable: 550 Permission denied.
test.talia.net:21 - Login successful.
Exception in thread Thread-1:
Traceback (most recent call last):
File "ftp.py", line 25, in check_ftp
ftp.mkd(test_folder)
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 641, in mkd
resp = self.voidcmd('MKD ' + dirname)
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 276, in voidcmd
return self.voidresp()
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 249, in voidresp
resp = self.getresp()
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 244, in getresp
raise error_perm(resp)
ftplib.error_perm: 550 test_folder: Permission denied
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "ftp.py", line 47, in run
check_ftp(options)
File "ftp.py", line 31, in check_ftp
ftp.retrlines('LIST', contents.append)
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 466, in retrlines
with self.transfercmd(cmd) as conn, \
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 397, in transfercmd
return self.ntransfercmd(cmd, rest)[0]
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 359, in ntransfercmd
source_address=self.source_address)
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/socket.py", line 722, in create_connection
raise err
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/socket.py", line 713, in create_connection
sock.connect(sa)
我不明白为什么套接字超时,即使检查在一个线程下运行。另外,如果一个线程挂起太长时间怎么能终止它。提前谢谢。
答案 0 :(得分:1)
首先,这是有效的:
def run():
check_ftp(options[0])
check_ftp(options[1])
check_ftp(options[2])
我猜不了。看来FTP对象没有正确清理。使用with
操作可以更轻松地关闭FTP。见this example
from ftplib import FTP
with FTP("ftp1.at.proftpd.org") as ftp:
ftp.login()
ftp.dir()
此外,线程无法取消,但进程可以。切换到multiprocessing
库将允许它们被杀死。