在 python FTP 脚本中构建错误检查功能
Building an error checking function in a python FTP script
我正在编写一个脚本,该脚本连接 "server" 计算机中的多个 "client" 计算机,然后使用这些客户端处理多个文件,使用 FTP(pyftplib 和 pyftpdlib ) 用于传输文件和结果。
该脚本通过在服务器上创建 3 个文件夹来工作:文件、处理和结果。客户端然后通过FTP连接到服务器,访问"Files"文件夹,获取文件进行处理,然后在处理过程中将其传输到"Processing"文件夹。然后,当它完成处理时,客户端从处理文件夹中删除文件并将结果复制到 "Results" 文件夹。
这在服务器端和客户端都正常工作。我遇到的问题是,如果其中一个客户端在中途断开连接而没有产生错误(PC 断开连接、断电),服务器将威胁这一点,就好像客户端仍在处理文件一样,文件将保留在"Processing" 文件夹。我想要的是一个错误检查功能,当发生这种情况时,"Processing" 文件夹中的文件将 return 到 "Files" 文件夹。
这是服务器FTP代码
def main():
authorizer = DummyAuthorizer()
authorizer.add_user('client', 'password', '.', perm='elradfmwM')
authorizer.add_anonymous(os.getcwd())
handler = FTPHandler
handler.authorizer = authorizer
handler.banner = "FTP Server."
address = ('', port)
server = FTPServer(address, handler)
server.max_cons = 256
server.max_cons_per_ip = 50
server.serve_forever()
if __name__ == '__main__':
main()
这是客户端 FTP 代码:
while True:
ftp = ftplib.FTP()
ftp.connect(arguments.host_ip, arguments.host_port)
ftp.login("client", "password")
print ftp.getwelcome()
ftp.retrlines('LIST')
ftp.retrbinary('RETR Output.txt', open('Output.txt', 'wb').write)
ftp.retrbinary('RETR dicionario.json', open('dicionario.json', 'wb').write)
with open('dicionario.json') as json_file:
json_data = json.load(json_file)
receptor_file = json_data['--receptor']
print 'Retrieving receptor file ' + receptor_file
ftp.retrbinary('RETR ' + receptor_file, open(receptor_file, 'wb').write)
ftp.cwd('Files')
ftp.retrlines('LIST')
filename = ftp.nlst()[0]
print 'Getting ' + filename
ftp.retrbinary('RETR ' + filename, open(filename, 'wb').write)
with open("Output.txt", "a") as input_file:
input_file.write('ligand = %s' %filename)
input_file.close()
ftp.delete(filename)
ftp.cwd('../Processing')
ftp.storbinary('STOR ' + filename, open(filename, 'rb'))
ftp.quit()
print "Processing"
return_code = subprocess.call(calls the program for processing files)
if return_code == 0:
print """Done!"""
ftp.connect(arguments.host_ip, arguments.host_port)
ftp.login("client", "password")
ftp.cwd('Results')
ftp.storbinary('STOR ' + os.path.splitext(filename)[0] + '_out.pdbqt', open (os.path.splitext(filename)[0] + '_out.pdbqt'))
ftp.cwd('../Processing')
ftp.delete(filename)
ftp.quit()
else:
print """Something is technically wrong..."""
ftp.connect(arguments.host_ip, arguments.host_port)
ftp.login("client", "password")
ftp.cwd('Files')
ftp.storbinary('STOR ' + filename, open(filename, 'rb'))
ftp.cwd('../Processing')
ftp.delete(filename)
ftp.quit()
感谢您的帮助!
所以,经过半个月的摆弄这段代码,我终于在客户端取消连接时使它起作用
首先,我必须让服务器能够识别每个客户端。我没有让他们只用一个用户登录,而是为每个连接创建了特定的用户,具有 2 个不同的功能:
def handler_generation(size=9, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for i in range (size))
这会生成 9 个字符的登录名和密码
然后我在 pyftpdlib 中创建了一个自定义处理程序,并使用了 on_login 函数:
class MyHandler(FTPHandler):
def on_login(self, username):
if username == "client":
user_login = handler_generation()
user_password = handler_generation()
global authorizer
authorizer.add_user(user_login, user_password, '.', perm='elradfmwM')
credentials = open("Credentials.txt",'w')
credentials.write(user_login)
credentials.write("\n")
credentials.write(user_password)
credentials.close()
else:
pass
因此,当客户端连接到 "client" 登录名时,服务器会生成一个 9 字符的登录名和密码,并将其发送到 "Credentials.txt" 文件中的客户端。在客户端,它会这样做:
ftp.login("client", "password")
ftp.retrbinary('RETR Credentials.txt', open('Credentials.txt', 'wb').write)
ftp.quit()
with open('Credentials.txt') as credential_file:
lines = credential_file.readlines()
credential_login = lines[0].split("\n")[0]
credential_password = lines[1].split("\n")[0]
ftp.connect(arguments.host_ip, arguments.host_port)
ftp.login(credential_login, credential_password)
所以现在所有客户端都使用自己的特定登录名进行连接。在客户端,我这样做是为了让每个完成的任务,客户端都会发送一个以其特定登录名命名的文件。我还让客户端在他们正在处理的文件中附加他们自己的登录名,以便服务器轻松找到文件:
ftp.rename(filename, credential_login + filename)
然后,我使用了处理程序 class 的另一个函数,on_disconnect:
def on_disconnect(self):
if self.username == "client":
pass
else:
if os.path.isfile(self.username):
pass
else:
for fname in os.listdir("Processing"):
if fname.startswith(self.username):
shutil.move("Processing/" + fname, "Files")
os.rename("Files/" + fname, "Files/" + fname[9::])
print self.remote_ip, self.remote_port,self.username, "disconnected"
pass
现在,每当客户端断开连接时,服务器都会搜索文件夹以检查客户端是否发送了处理程序文件。如果不存在,服务器会将文件移动到 "Files" 文件夹,这是尚未处理的文件所在的文件夹。
为了使失败的客户端在不发送退出命令的情况下与服务器断开连接,我使用了 pyftpdlib 的超时功能。为了确保活动客户端不会意外超时,我在客户端中实现了一个线程,每 N 秒对服务器执行一次操作:
class perpetualTimer():
def __init__(self,t,hFunction):
self.t=t
self.hFunction = hFunction
self.thread = Timer(self.t,self.handle_function)
def handle_function(self):
self.hFunction()
self.thread = Timer(self.t,self.handle_function)
self.thread.start()
def start(self):
self.thread.start()
def cancel(self):
self.thread.cancel()
def NotIdle():
Doing something here
t = perpetualTimer(10, NotIdle)
t.start()
(我直接从某人这里复制的这个特定代码)
瞧。现在服务端和客户端都可以工作,并且有自己的错误检查功能。
我把这个答案放在这里以防有人遇到类似的问题。
谢谢!
我正在编写一个脚本,该脚本连接 "server" 计算机中的多个 "client" 计算机,然后使用这些客户端处理多个文件,使用 FTP(pyftplib 和 pyftpdlib ) 用于传输文件和结果。
该脚本通过在服务器上创建 3 个文件夹来工作:文件、处理和结果。客户端然后通过FTP连接到服务器,访问"Files"文件夹,获取文件进行处理,然后在处理过程中将其传输到"Processing"文件夹。然后,当它完成处理时,客户端从处理文件夹中删除文件并将结果复制到 "Results" 文件夹。
这在服务器端和客户端都正常工作。我遇到的问题是,如果其中一个客户端在中途断开连接而没有产生错误(PC 断开连接、断电),服务器将威胁这一点,就好像客户端仍在处理文件一样,文件将保留在"Processing" 文件夹。我想要的是一个错误检查功能,当发生这种情况时,"Processing" 文件夹中的文件将 return 到 "Files" 文件夹。
这是服务器FTP代码
def main():
authorizer = DummyAuthorizer()
authorizer.add_user('client', 'password', '.', perm='elradfmwM')
authorizer.add_anonymous(os.getcwd())
handler = FTPHandler
handler.authorizer = authorizer
handler.banner = "FTP Server."
address = ('', port)
server = FTPServer(address, handler)
server.max_cons = 256
server.max_cons_per_ip = 50
server.serve_forever()
if __name__ == '__main__':
main()
这是客户端 FTP 代码:
while True:
ftp = ftplib.FTP()
ftp.connect(arguments.host_ip, arguments.host_port)
ftp.login("client", "password")
print ftp.getwelcome()
ftp.retrlines('LIST')
ftp.retrbinary('RETR Output.txt', open('Output.txt', 'wb').write)
ftp.retrbinary('RETR dicionario.json', open('dicionario.json', 'wb').write)
with open('dicionario.json') as json_file:
json_data = json.load(json_file)
receptor_file = json_data['--receptor']
print 'Retrieving receptor file ' + receptor_file
ftp.retrbinary('RETR ' + receptor_file, open(receptor_file, 'wb').write)
ftp.cwd('Files')
ftp.retrlines('LIST')
filename = ftp.nlst()[0]
print 'Getting ' + filename
ftp.retrbinary('RETR ' + filename, open(filename, 'wb').write)
with open("Output.txt", "a") as input_file:
input_file.write('ligand = %s' %filename)
input_file.close()
ftp.delete(filename)
ftp.cwd('../Processing')
ftp.storbinary('STOR ' + filename, open(filename, 'rb'))
ftp.quit()
print "Processing"
return_code = subprocess.call(calls the program for processing files)
if return_code == 0:
print """Done!"""
ftp.connect(arguments.host_ip, arguments.host_port)
ftp.login("client", "password")
ftp.cwd('Results')
ftp.storbinary('STOR ' + os.path.splitext(filename)[0] + '_out.pdbqt', open (os.path.splitext(filename)[0] + '_out.pdbqt'))
ftp.cwd('../Processing')
ftp.delete(filename)
ftp.quit()
else:
print """Something is technically wrong..."""
ftp.connect(arguments.host_ip, arguments.host_port)
ftp.login("client", "password")
ftp.cwd('Files')
ftp.storbinary('STOR ' + filename, open(filename, 'rb'))
ftp.cwd('../Processing')
ftp.delete(filename)
ftp.quit()
感谢您的帮助!
所以,经过半个月的摆弄这段代码,我终于在客户端取消连接时使它起作用
首先,我必须让服务器能够识别每个客户端。我没有让他们只用一个用户登录,而是为每个连接创建了特定的用户,具有 2 个不同的功能:
def handler_generation(size=9, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for i in range (size))
这会生成 9 个字符的登录名和密码
然后我在 pyftpdlib 中创建了一个自定义处理程序,并使用了 on_login 函数:
class MyHandler(FTPHandler):
def on_login(self, username):
if username == "client":
user_login = handler_generation()
user_password = handler_generation()
global authorizer
authorizer.add_user(user_login, user_password, '.', perm='elradfmwM')
credentials = open("Credentials.txt",'w')
credentials.write(user_login)
credentials.write("\n")
credentials.write(user_password)
credentials.close()
else:
pass
因此,当客户端连接到 "client" 登录名时,服务器会生成一个 9 字符的登录名和密码,并将其发送到 "Credentials.txt" 文件中的客户端。在客户端,它会这样做:
ftp.login("client", "password")
ftp.retrbinary('RETR Credentials.txt', open('Credentials.txt', 'wb').write)
ftp.quit()
with open('Credentials.txt') as credential_file:
lines = credential_file.readlines()
credential_login = lines[0].split("\n")[0]
credential_password = lines[1].split("\n")[0]
ftp.connect(arguments.host_ip, arguments.host_port)
ftp.login(credential_login, credential_password)
所以现在所有客户端都使用自己的特定登录名进行连接。在客户端,我这样做是为了让每个完成的任务,客户端都会发送一个以其特定登录名命名的文件。我还让客户端在他们正在处理的文件中附加他们自己的登录名,以便服务器轻松找到文件:
ftp.rename(filename, credential_login + filename)
然后,我使用了处理程序 class 的另一个函数,on_disconnect:
def on_disconnect(self):
if self.username == "client":
pass
else:
if os.path.isfile(self.username):
pass
else:
for fname in os.listdir("Processing"):
if fname.startswith(self.username):
shutil.move("Processing/" + fname, "Files")
os.rename("Files/" + fname, "Files/" + fname[9::])
print self.remote_ip, self.remote_port,self.username, "disconnected"
pass
现在,每当客户端断开连接时,服务器都会搜索文件夹以检查客户端是否发送了处理程序文件。如果不存在,服务器会将文件移动到 "Files" 文件夹,这是尚未处理的文件所在的文件夹。
为了使失败的客户端在不发送退出命令的情况下与服务器断开连接,我使用了 pyftpdlib 的超时功能。为了确保活动客户端不会意外超时,我在客户端中实现了一个线程,每 N 秒对服务器执行一次操作:
class perpetualTimer():
def __init__(self,t,hFunction):
self.t=t
self.hFunction = hFunction
self.thread = Timer(self.t,self.handle_function)
def handle_function(self):
self.hFunction()
self.thread = Timer(self.t,self.handle_function)
self.thread.start()
def start(self):
self.thread.start()
def cancel(self):
self.thread.cancel()
def NotIdle():
Doing something here
t = perpetualTimer(10, NotIdle)
t.start()
(我直接从某人这里复制的这个特定代码)
瞧。现在服务端和客户端都可以工作,并且有自己的错误检查功能。
我把这个答案放在这里以防有人遇到类似的问题。
谢谢!