2016-04-12 49 views
-1

我正在研究一個腳本,它連接「服務器」計算機中的幾臺「客戶端」計算機,然後使用這些客戶端來處理多個文件,使用FTP(pyftplib和pyftpdlib )用於傳輸文件和結果。構建python FTP腳本中的錯誤檢查功能

該腳本的工作原理是在服務器上創建3個文件夾:文件,處理和結果。然後,客戶端通過FTP連接到服務器,訪問「文件」文件夾,獲取文件進行處理,然後在處理文件時將其傳輸到「處理」文件夾。然後,當它完成處理時,客戶端從處理文件夾中刪除文件並將結果複製到「結果」文件夾中。

這是工作正常,無論是在服務器端和客戶端。我遇到的問題是,如果其中一個客戶端中途斷開連接而沒有產生錯誤(PC斷開連接,斷電),服務器將威脅到這一點,就好像客戶端仍在處理該文件一樣,該文件將保持在「處理」文件夾。我想要的是一個錯誤檢查功能,當發生這種情況時,「Processing」文件夾中的文件將返回到「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() 

感謝您的幫助!

回答

0

所以,半個月與此代碼擺弄之後,我終於取得了當客戶端取消連接

首先,我不得不做出的服務器,以確定每個客戶端的方式發揮作用。而不是使他們僅與一個用戶登錄的,我創建特定用戶針對每個連接,具有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 

所以,當客戶端與「客戶端」的登錄連接,所述服務器生成的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) 

因此,現在客戶端都與自己的特定登錄連接。在客戶端,我這樣做是爲了完成每個任務,客戶端將發送一個文件命名爲他們的具體登錄。我也做了客戶在文件中添加自己的登錄名,他們處理很容易讓服務器來查找文件:

​​

然後,我用的處理程序類的其他功能,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 

現在,無論客戶端何時斷開,服務器都會搜索文件夾以檢查客戶端是否發送了處理程序文件。如果不存在,服務器會將文件移動到「文件」文件夾,該文件夾是尚未處理的文件所在的文件夾。

爲了使一個失敗的客戶端從服務器斷開連接而不發送退出命令,我使用了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() 

(這個特殊的代碼我從別人直接複製這裏)

瞧。現在服務器和客戶端都工作並且具有它們自己的錯誤檢查功能。

我在這裏提供這個答案,以防有人遇到類似問題。

謝謝!