1. 程式人生 > >python實現一個簡單的ftp程式

python實現一個簡單的ftp程式

客戶端可以向伺服器端下載,上傳檔案

下載檔案指令:get_filename

上傳檔案指令:put_filename

ftp工程目錄:

ftp工作原理:

put指令工作原理圖:

put指令工作原理圖:

 

程式碼:

客戶端:

import socket,os,json
class my_ftp_client(object):
    def __init__(self):
        self.client = socket.socket()

    def help(self):
        msg = '''
        get filename
        put filename
        '''
    def connect(self,ip,port): # 與伺服器端建立連線,ip代表伺服器端的ip地址,port代表伺服器端的埠
        self.client.connect((ip,port))

    def interaction(self): # 讓使用者輸入執行,然後呼叫對應的函式來與伺服器端進行互動
        while True:
            data = input("<<").strip() # 使用者輸入指令
            if len(data)==0 : # 如果指令輸入有誤,重新輸入
                continue
            data_one = data.split()[0] # 得到使用者輸入的操作,如果使用者如果輸入'put filename'(往伺服器端傳檔名為filename的檔案),那麼data_one就是'put'
            if hasattr(self,"cmd_%s"% data_one): #如果我們寫的這個類中有名為'cmd_put'的函式,就執行下面的程式碼
                func = getattr(self,"cmd_%s"% data_one) # func實際上就是'cmd_put'
                func(data) # 呼叫類中名為'cmd_put'的函式
            else :
                self.help()

    def cmd_put(self,*args):
        data_split = args[0].split() # 得到使用者輸入的指令
        if len(data_split) > 1:      # 確保指令是正確的
            filename = data_split[1] # 得到檔名
            if os.path.isfile(filename): # 這是個檔案
                filesize = os.stat(filename).st_size
                msg = {                 #定義一個json文字,裡面儲存的是檔案的一些資訊
                    'action':'put',     #檔案對客戶端的操作
                    'filename':filename,#檔名
                    'filesize':filesize,#檔案大小
                }
                self.client.send(json.dump(msg).encode()) #將檔案的資訊一次性傳給伺服器端,這個地方dump是將json文字型別轉化為其他儲存格式
                print('等待伺服器端返回確認指令!')
                server_response = self.client.recv(1024) # 接收伺服器端傳送來的確認指令
                f = open(filename,'wb')
                for line in f: # 遍歷檔案,將檔案內容傳送給伺服器端
                    self.client.send(line)
                else :
                    print("file end!")
        else : #指令是錯誤的
            print("instruction error!")

    def cmd_get(self,*args): # 這個函式是從客戶端得到名為filename的檔案
        data_split = args[0].split() # 得到使用者輸入的指令
        if len(data_split) > 1: # 指令正確,執行下面的操作
            file_name = data_split[1] # 得到檔名
            msg = {  # 定義一個json文字,裡面儲存的是檔案的一些資訊
                'action': 'get',  # 檔案對客戶端的操作
                'filename': file_name,  # 檔名
            }
            self.client.send( json.dump(msg).encode() ) # 給伺服器端傳送這個檔案的資訊
            print('等待伺服器端返回確認指令!')
            recevice_data = self.client.recv(1024).strip() # 接收伺服器端傳送回來的指令
            data_json = json.loads(recevice_data.decode()) # 將recevice_data解析出來
            One_data = data_json["instruction"] # 得到伺服器端發回來的第一個資訊
            if One_data == 'Not Find': # 如果第一個資訊為'Not Find',說明伺服器端沒有名為file_name的檔案
                return
            else : # 否則,有名為file_name的檔案
                file_size = data_json["file_size"] # 得到這個檔案的大小
                now_size = 0 # 當前接收到的檔案的大小
                f = open(file_name,'wb')
                while now_size < file_size : # 開始接收檔案
                    data = self.client.recv(1024)
                    f.write(data)
                    now_size += len(data)
                else :
                    print("檔案接收完畢!")

ftp = my_ftp_client()
ftp.connect('localhost',9999)
ftp.interaction()

伺服器端:

import os,socketserver,json
class MyTCPHandler(socketserver.BaseRequestHandler):
    def put(self,*args):
        data_json = args[0] # 得到json文字
        filename = data_json['filename'] # 得到檔名
        filesize = data_json['filesize'] # 得到檔案的大小
        now_size = 0 # 這是伺服器端已經接收到的檔案的大小
        if os.path.isfile(filename): # 如果伺服器端已經存在名為filename的檔案,就新建一個名為'filename.new'的檔案
            f = open(filename + '.new','wb')
        else : # 否則,就建出名為filename的檔案
            f = open(filename,'wb')
        self.request.send(b'200 ojbk') # 給客戶端返回伺服器端已經準備好接受檔案的資訊
        while now_size < filesize: # 開始接收檔案
            data = self.request.recv(1024)
            f.write(data) # 將接收到的檔案的內容寫入到伺服器端中的名為filename的檔案中去
            now_size += len(data) # now_size加
        else :
            print("file recv over!") # 檔案接收完畢
    def get(self,*args):
        data_json = args[0] # 得到客戶端傳送來的資訊
        file_name = data_json['filename'] # 得到客戶端想要從伺服器端獲取的檔案的檔名
        if os.path.isfile(file_name): # 伺服器端有這個檔案
            filesize = os.stat(file_name).st_size # 得到這個檔案的大小
            msg = {  #將這個檔案的資訊傳給伺服器端
                "instruction": 'Find the file', # 這個資訊說明伺服器端已經找到了這個檔案了
                "file_size": filesize # 返回給客戶端這個檔案的大小
            }
            self.request.send( json.dumps(msg).encode())
            f =open(file_name,'rb') # 開啟這個檔案
            for line in f: # 給客戶端傳送檔案內容
                self.request.send(line)
            else :
                print("檔案傳輸完畢!")
        else : # 伺服器端沒有這個檔案
            msg = {
                "instruction": 'Not Find', # 這個資訊說明伺服器端沒有找到這個檔案
                'file_size': 0 # 檔案大小為0
            }
            self.request.send( json.dumps(msg).encode() )
    def handle(self):
        while True:
            try:
                self.data = self.request.recv(1024).strip()
                print("{} wrote".format(self.client_address[0]))  # 列印客戶端的ip地址
                print("data:", self.data)
                data_json = json.loads(self.data.decode())
                action = data_json['action'] # 得到要執行的操作
                if hasattr(self,action): # 這個類中有名為action的函式
                    func = getattr(self,action) # func就相當於是'action'
                    func(data_json)
            except ConnectionResetError as e: # 捕捉到錯誤了
                print("err", e)
                break

HOST, PORT = "localhost", 9999
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()