從某寶上購買了一份《Python神經網絡深度學習》課程,按照視頻教程,用python語言,寫了一個簡易的FTP服務端和客戶端程序,之前也用C++寫過聊天程序,編程思路差很少,可是python編程時更順暢,代碼量更少。沒有很高深的理論知識,也不須要紮實的編程基礎,知道須要用哪些庫就好了。html
兩種語言對比,初次感覺到python語言的易用之處,python的核心是簡潔清晰,也是僞代碼的最佳實踐語言。能夠理解爲C語言的一些經常使用功能庫的轉義,因此廣泛認爲python語言帶來編程便捷的同時,也會帶來一部分性能的犧牲。但這並不也是絕對的,有時候程序的開發效率更爲重要,更況且並不必定人人都是大牛,用C或者其它語言未必就能寫出比python更高性能的代碼。總體學習完之後,還會再來深刻對比下python和其餘語言的差別。python
登錄驗證編程
上傳:支持斷點上傳,MD5驗證json
簡單命令:cd、ls、mkdir服務器
下面是我彙總的用到的模塊信息,要寫出完整的代碼,首先得了解基本的python函數和庫,例如:網絡
os,sys庫對目錄和文件的處理;app
socket和socketserver對網絡通訊的處理;socket
json對傳輸數據格式的處理;ide
configparse對配置文件的處理;函數
hashlib對MD5校驗的處理;
optparse對命令行輸入參數的格式化處理等;
另外還須要瞭解如何利用反射機制,處理多If else分支的函數調用等。
關於python反射機制,參考連接:http://www.javashuo.com/article/p-vddihgho-by.html
ftp_client.py:FTP客戶端主程序
import socket import optparse import json import os,sys import hashlib STATUS_CODE = { 250 : "Invalid cmd format, e.g: {'action':'get','filename':'test.py','size':344}", 251 : "Invalid cmd ", 252 : "Invalid auth data", 253 : "Wrong username or password", 254 : "Passed authentication", 255 : "Filename doesn't provided", 256 : "File doesn't exist on server", 257 : "ready to send file", 258 : "md5 verification", 800 : "the file exist,but not enough ,is continue? ", 801 : "the file exist !", 802 : " ready to receive datas", 900 : "md5 valdate success" } sk = socket.socket() sk.connect(("127.0.0.1",18000)) class ClientHander(): def __init__(self): #初始化變量 self.op = optparse.OptionParser() self.op.add_option("-s","--server",dest="server") self.op.add_option("-P", "--port", dest="port") self.op.add_option("-u", "--username", dest="username") self.op.add_option("-p", "--password", dest="password") self.options,self.args = self.op.parse_args() #self.verify_args(self.options,self.args) self.mainPath = os.path.dirname(os.path.abspath(__file__)) self.last = 0 self.is_authenticated = False self.is_connected = False self.is_needed_md5 = True #開始創建鏈接 self.make_connection() # 驗證用戶輸入信息 def verify_args(self,options,args): server = options.server port = options.port username = options.username password = options.password if int(port) > 0 and int(port) < 65535: return True else: exit("the port should been in 0-65535") # 鏈接服務器 def make_connection(self): if not self.is_connected: self.sock = socket.socket() self.sock.connect(("127.0.0.1", 18000)) self.is_connected = True return def interactive(self): # 判斷是否已經驗證過 if not self.is_authenticated: self.make_connection() self.authenticate() # 開始和服務器進行交互 cmd_info = input("[%s]"%self.current_dir).strip() # 利用反射機制,創建命令與功能對應關係 cmd_list = cmd_info.split() if hasattr(self,cmd_list[0]): func = getattr(self,cmd_list[0]) func(*cmd_list) def authenticate(self): if not self.is_connected: print("沒有鏈接FTP服務器") return if self.options.username is None or self.options.password is None: username = input("username:") password = input("password:") return self.get_auth_result(username,password) return self.get_auth_result(self.options.username,self.options.password) def response(self): data = self.sock.recv(1024).decode("utf-8") data = json.loads(data) return data def get_auth_result(self,username,password): data = { "action":"auth", "username":username, "password":password } self.sock.send(json.dumps(data).encode("utf-8")) res = self.response() print("res",res["status_code"]) if res["status_code"] == 254: self.username = username self.current_dir = username self.is_authenticated = True print(STATUS_CODE[254]) return True else: print(STATUS_CODE[res["status_code"]]) def put(self,*cmd_list): # 1 發送上傳命令 action,local_path,target_path = cmd_list local_path = os.path.join(self.mainPath,local_path) file_name = os.path.basename(local_path) file_size = os.stat(local_path).st_size data = { "action":"put", "file_name": file_name, "file_size": file_size, "target_path": target_path } self.sock.sendall(json.dumps(data).encode("utf-8")) # 2 接收服務器檢查結果 has_sent = 0 is_exsit = self.sock.recv(1024).decode("utf-8") # 3 根據服務器檢查結果,執行相應命令 # 文件存在但不完整 if is_exsit == "800": print("服務器文件已經存在,且不完整") # 和服務器確認是否繼續上傳 choice = input("The file exist but not enough,is continue?[Y/N]").strip() # 繼續上傳 if choice.upper() == "Y": self.sock.sendall("Y".encode("utf-8")) # 等待服務器返回存在文件大小 continue_position = self.sock.recv(1024).decode("utf-8") has_sent += int(continue_position) # 不繼續上傳 else: self.sock.sendall("N".encode("utf-8")) # 文件存在且完整 elif is_exsit == "801": print("服務器文件已經存在,且完整") return f = open(local_path,"rb") f.seek(has_sent) md5_obj = None if self.is_needed_md5: md5_obj = hashlib.md5() while has_sent < file_size: data = f.read(1024) self.sock.sendall(data) if md5_obj: md5_obj.update(data) has_sent += len(data) mdt_val_server = self.sock.recv(1024).decode("utf-8") mdt_val_client = md5_obj.hexdigest() self.show_progress(has_sent,file_size) print("client's md5 is %s"%mdt_val_client) print("server's md5 is %s" %mdt_val_server) f.close() if mdt_val_client == mdt_val_server: print("put success") else: print("Athenticate MD5 failed.") def show_progress(self,has,total): rate = float(has) / float(total) rate_num = int(rate*100) print("%s%% %s\r"%(rate_num,"#"*rate_num)) def ls(self,*cmd_list): data = { "action":"ls" } self.sock.sendall(json.dumps(data).encode("utf-8")) data = self.sock.recv(1024).decode("utf-8") print(data) def cd(self,*cmd_list): # cd images data = { "action":"cd", "dirname":cmd_list[1] } self.sock.sendall(json.dumps(data).encode("utf-8")) data = self.sock.recv(1024).decode("utf-8") self.current_dir = os.path.basename(data) print(os.path.basename(data)) def mkdir(self,*cmd_list): data = { "action":"mkdir", "dirname":cmd_list[1] } self.sock.sendall(json.dumps(data).encode("utf-8")) data = self.sock.recv(1024).decode("utf-8") print(data) ch = ClientHander() while 1: ch.interactive()
ftp_server.py:FTP服務器端主程序
import os,sys PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(PATH) from core import main if __name__ == '__main__': main.ArgvHandler()
main.py:處理參數輸入
import optparse import socketserver from conf import settings from core import server class ArgvHandler(): def __init__(self): self.op = optparse.OptionParser() #self.op.add_option("-s","--server",dest = "server") #self.op.addd_option("-P", "--port", dest="port") options,args = self.op.parse_args() self.verify_args(options,args) def verify_args(self,options,args): #首先判斷參數的長度 if len(args) < 1: print("參數個數太少,請從新輸入.") else: cmd = args[0] if hasattr(self,cmd): func = getattr(self,cmd) func() self.start() def start(self): s = socketserver.ThreadingTCPServer((settings.IP,settings.PORT),server.ServerHandler) print("服務器已經啓動") s.serve_forever() def help(self): pass
server.py:具體代碼實現
import socketserver import json import configparser from conf import settings import os import hashlib STATUS_CODE = { 250 : "Invalid cmd format, e.g: {'action':'get','filename':'test.py','size':344}", 251 : "Invalid cmd ", 252 : "Invalid auth data", 253 : "Wrong username or password", 254 : "Passed authentication", 255 : "Filename doesn't provided", 256 : "File doesn't exist on server", 257 : "ready to send file", 258 : "md5 verification", 800 : "the file exist,but not enough ,is continue? ", 801 : "the file exist !", 802 : " ready to receive datas", 900 : "md5 valdate success" } class ServerHandler(socketserver.BaseRequestHandler): def handle(self): while 1: data = self.request.recv(1024).strip() if not data:continue data = json.loads(data.decode("utf-8")) if data.get("action"): if hasattr(self,data.get("action")): func = getattr(self,data.get("action")) func(**data) else: print("Invalid cmd") else: print("Invalid cmd") def send_response(self,status_code): response = {"status_code":status_code} self.request.sendall(json.dumps(response).encode("utf8")) def auth(self,**data): username = data["username"] password = data["password"] print("收到客戶端驗證請求,用戶名:",username,"密碼:",password) user = self.authenticate(username,password) if user: self.send_response(254) else: self.send_response(253) def authenticate(self,username,password): cfg = configparser.ConfigParser() cfg.read(settings.ACCOUNT_PATH) print("正在讀取配置用戶配置信息:",settings.ACCOUNT_PATH) if username in cfg.sections(): if cfg[username]["Password"] == password: self.useranme = username self.mainPath = os.path.join(settings.BASE_DIR,"home",self.useranme) print("成功匹配上用戶信息,用戶密碼:", cfg[username]["Password"]) print("用戶根目錄爲:",self.mainPath) return username def put(self,**data): print("data",data) file_name = data.get("file_name") file_size = data.get("file_size") target_path = data.get("target_path") abs_path = os.path.join(self.mainPath,target_path,file_name) has_received = 0 if os.path.exists(abs_path): file_has_size = os.stat(abs_path).st_size if file_has_size < file_size: #斷點續傳 self.request.sendall("800".encode("utf-8")) choice = self.request.recv(1024).decode("utf-8") if choice.upper() == "Y": self.request.sendall(str(file_has_size).encode("utf-8")) f = open(abs_path,"ab") has_received += file_has_size else: f = open(abs_path,"wb") else: #文件徹底存在 self.request.sendall("801".encode("utf-8")) return else: self.request.sendall("802".encode("utf-8")) f = open(abs_path, "wb") md5_obj = hashlib.md5() while has_received < file_size: try: data = self.request.recv(1024) f.write(data) md5_obj.update(data) has_received += len(data) except Exception as e: print("傳輸文件發生異常:",str(e)) break # 給客戶端發送MD5 md5_val = md5_obj.hexdigest() self.request.sendall(md5_val.encode("utf-8")) f.close() def ls(self,**data): file_list = os.listdir(self.mainPath) print("ls %s"%self.mainPath) if not file_list: file_str = "<empty dir>" else: file_str = "\n".join(file_list) self.request.sendall(file_str.encode("utf-8")) def cd(self,**data): dir_name = data.get("dirname") print("cd %s\n"%dir_name) if dir_name == "..": self.mainPath = os.path.dirname(self.mainPath) else: self.mainPath = os.path.join(self.mainPath,dir_name) self.request.sendall(self.mainPath.encode("utf-8")) def mkdir(self,**data): dirname = data.get("dirname") path = os.path.join(self.mainPath,dirname) if not os.path.exists(path): if "/" in dirname: os.makedirs(path) else: os.mkdir(path) self.request.sendall("create success".encode("utf-8")) else: self.request.sendall("dirname exist".encode("utf-8"))
accounts.cfg:帳戶信息,登錄驗證用
[DEFAULT] [tanbiao] Password = tanbiao Quotation = 100 [root] Password = root Quotation = 100
setting.py:配置信息
import os BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) IP="127.0.0.1" PORT=18000 ACCOUNT_PATH=os.path.join(BASE_DIR,"conf","accounts.cfg")