python之路——作業:Select FTP(僅供參考)
阿新 • • 發佈:2017-07-15
view info warn phi socket split list 開始 序號
一、作業需要
使用SELECT或SELECTORS模塊實現並發簡單版FTP
允許多用戶並發上傳下載文件
二、README
程序實現了以下功能:
1、用戶登錄註冊(測試用戶:japhi、alex;密碼都是123)
2、上傳/下載文件(已有示例文件)
3、查看不同用戶自己家得目錄下文件
4、使用了selector模塊,實現單線程下並發效果
客戶端啟動程序:Client_start.py
服務端啟動程序:Server_start.py
三、目錄說明
Select FTP/ |-- Select FTPClient/ #客戶端文件夾 | |-- 示例文件夾/ #客戶端上傳/下載示例文件夾 | |-- Client_start.py #客戶端啟動程序 | |-- FTPServer/ #服務端文件夾 | |-- bin/ | | |-- __init__.py | | |-- Server_start.py #程序啟動的主入口 | | | |-- conf/ | | |-- setting.py #配置文件 | | | |-- db/ #用戶數據 | | |-- alex #用戶名alex的數據文件夾 | | |-- japhi #用戶名japhi的數據文件夾 | | | |-- home/ | | |-- alex/ #用戶alex用戶家目錄 | | |-- japhi/ #用戶japhi用戶家目錄 | | |-- log/ | |-- log_sys.log #日誌文件 | |-- src/ | |-- __init__.py | |-- common.py #公共函數功能 | |-- main.py #程序主函數 | |-- user.py #用戶類及方法 | |-- FTP.png #流程圖 |-- README.txt
四、流程圖
五、代碼說明
1、Select FTPClient/Client_start.py
import socket,os,sys,time Basedir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),"FTPServer") updir = os.path.join(os.path.dirname(os.path.abspath(__file__)),"示例文件夾") sys.path.append(Basedir) HOST = "localhost" PORT = 6960 def upload(client,user_info,name): ‘‘‘ 客戶端上傳文件的函數 :param client:scoket客戶端標誌 :param user_info:客戶端登陸用戶的信息 :param name:客戶端登陸用戶的名字 :return:none ‘‘‘ print("\033[1;37m當前可選上傳\033[0m".center(40,"*")) dic = {} for root, dirs, files in os.walk(updir): for i,j in enumerate(files): k = i+1 dic[k] = j print("\033[1;37m%s:%s\033[0m"%(k,j)) choice = input("請輸入要上傳的文件序號:>>>") command = "upload+"+user_info client.send(bytes(command,encoding="utf-8")) res = client.recv(1024) if str(res,encoding="utf-8") == "True": dir = os.path.join(updir,dic[int(choice)]) f = open(dir,"rb") data = f.read() length = str(len(data)) command2 = "uploadfile+"+user_info+"+"+length+"+"+dic[int(choice)] client.send(command2.encode("utf-8")) client.recv(1024) # time.sleep(0.5) client.sendall(data) time.sleep(1) print("\033[1;37m文件上傳成功\033[0m") f.close() def download(client,user_info,name): ‘‘‘ 客戶端下載文件的函數 :param client: scoket客戶端標誌 :param user_info: 客戶端登陸的用戶信息 :param name:客戶端登陸的用戶名字 :return: none ‘‘‘ dic = {} command = "download+"+user_info client.sendall(bytes(command, encoding="utf-8")) data = client.recv(4069) res = eval(str(data, encoding="utf-8")) if len(res) == 0: print("\033[1;31m當前目錄下暫無文件\033[0m".center(40, "-")) else: for i,j in enumerate(res): k = i + 1 dic[k] = j print("\033[1;37m%s:%s\033[0m" % (k, j)) choice = input("請選擇要下載的文件序號:>>>") command2 = "downloadfile+"+user_info+"+"+dic[int(choice)] client.send(bytes(command2 , encoding="utf-8")) print("\033[1;37m準備開始下載文件\033[0m") dir = os.path.join(updir, dic[int(choice)]) res_length = str(client.recv(1024).decode()) # client.send("True".encode("utf-8")) #防止方式黏包 length = 0 f = open(dir, "wb") while length < int(res_length): if int(res_length) - length > 1024: size = 1024 else: size = int(res_length) - length data = client.recv(size) length += len(data) f.write(data) if length == int(res_length): time.sleep(1) print("\033[1;37m文件下載成功\033[0m") f.close() def view_file(client,user_info,name): ‘‘‘ 客戶端查看當前目錄下文件的函數 :param client: scoket客戶端標誌 :param user_info: 客戶端登陸的用戶信息 :param name: 客戶端登陸的用戶名字 :return: none ‘‘‘ command = "view+"+user_info client.sendall(bytes(command,encoding="utf-8")) data = client.recv(1024) res = eval(str(data,encoding="utf-8")) if len(res) == 0: print("\033[1;31m當前目錄下暫無文件\033[0m".center(40, "-")) else: for i in res: print("\033[1;35m%s\033[0m"%i) def operate(client,user_info,name): ‘‘‘ 客戶端操作主函數 :param client: scoket客戶端標誌 :param user_info: 客戶端登陸的用戶信息 :param name: 客戶端登陸的用戶名字 :return: none ‘‘‘ dic = {"1":upload,"2":download,"3":view_file} info = ‘‘‘------操作指令------ 1、上傳文件 2、下載文件 3、查看目錄下文件 4、退出 ‘‘‘ while True: print("\033[1;33m%s\033[0m" % info) choice = input("請輸入你要操作的命令:>>>").strip() if choice.isdigit() and 0 < int(choice) <= len(dic): dic.get(choice)(client,user_info,name) elif choice.isdigit() and int(choice) == 4: break else: print("\033[1;31m輸出錯誤\033[0m".center(40, "-")) def com_parse(client,com): ‘‘‘ 客戶端用戶登陸註冊命中解析函數 :param client: 客戶端scoket標誌 :param com: 命令 :return: 登陸成功返回True,否則False ‘‘‘ client.sendall(com.encode("utf-8")) re = client.recv(4096) if str(re,encoding="utf-8") == "Success": return True elif str(re, encoding="utf-8") == "Success": return False def login(client,data): ‘‘‘ 客戶端用戶登陸函數 :param client: 客戶端scoket標誌 :param data: 數據 :return: none ‘‘‘ name = input("請輸入您的名字:>>>").strip() psd = input("請輸入密碼:>>>").strip() user_info = name+"+"+psd com = "login+"+user_info if com_parse(client,com): print("\033[1;31m登陸成功\033[0m") operate(client,user_info,name) else: print("\033[1;31m登陸出現異常\033[0m") def register(client,data): ‘‘‘ 客戶端用戶註冊函數 :param client: 客戶端scoket標誌 :param data: 數據 :return: none ‘‘‘ name = input("請輸入您的名字:>>>").strip() psd = input("請輸入密碼:>>>").strip() com = "register+" + name + "+" + psd if com_parse(client, com): user_info = name + "+" + psd print("\033[1;31m註冊成功\033[0m") operate(client, user_info, name) else: print("\033[1;31m註冊出現異常\033[0m") def quit(client,data): ‘‘‘ 程序退出函數 :param client: 客戶端scoket標誌 :param data: 用戶數據 :return: none ‘‘‘ exit() def main_func(client): ‘‘‘ 客戶端主菜單函數 :param client: 客戶端scoket標誌 :param data: 數據 :return: none ‘‘‘ data = "連接成功" # client.send("succeed".encode("utf-8")) dic = {"1":login,"2":register,"3":quit} info = ‘‘‘------用戶登錄界面------*{0}* 1、登陸 2、註冊 3、退出 ‘‘‘.format(data) print("\033[1;33m%s\033[0m"%info) what = input("你要幹嘛?>>>").strip() if what.isdigit() and 0 < int(what) <= len(dic): dic.get(what)(client,data) else: print("\033[1;31m輸出錯誤\033[0m".center(40,"-")) if __name__ == ‘__main__‘: client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect((HOST,PORT)) main_func(client) client.close()
2、Select FTPServer/bin/Server_start.py
import os,sys BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(BASE_DIR) from src.main import func if __name__ == ‘__main__‘: func()
3、Select FTPServer/conf/settings.py
import os basedir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) user_home = "%s/home"%basedir user_info = "%s/db"%basedir log_dir = os.path.join(basedir,"log") HOST = "localhost" PORT = 6960
4、Select FTPServer/src/common.py
import logging,os,pickle,sys,uuid frame = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(frame) from conf import settings def sys_logging(content,levelname): ‘‘‘ 程序記錄日誌函數 :param content: 日誌的內容 :param levelname: 日誌的等級 :return: none ‘‘‘ _filename = os.path.join(settings.log_dir,"log_sys.log") log = logging.getLogger(_filename) logging.basicConfig(filename=_filename,level=logging.INFO,format=‘%(asctime)s-%(levelname)s-%(message)s‘, datefmt=‘%m/%d/%Y %I:%M:%S %p‘) if levelname == ‘debug‘: logging.debug(content) elif levelname == ‘info‘: logging.info(content) elif levelname == ‘warning‘: logging.warning(content) elif levelname == ‘error‘: logging.error(content) elif levelname == ‘critical‘: logging.critical(content) def show(msg,msg_type): ‘‘‘ 程序不同信息打印的字體顏色 :param msg: 打印信息 :param msg_type: 打印信息的類型 :return: none ‘‘‘ if msg_type == "info": show_msg = "\033[1;35m%s\033[0m"%msg elif msg_type == "error": show_msg = "\033[1;31m%s\033[0m"%msg elif msg_type == "msg": show_msg = "\033[1;37m%s\033[0m"%msg else: show_msg = "\033[1;32m%s\033[0m"%msg print(show_msg) sys_logging(msg,msg_type)
5、Select FTPServer/src/main.py
import socket,os,sys,selectors Base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(Base_dir) from conf import settings from src.common import show from src.user import User sel = selectors.DefaultSelector() def accept(server, mask): ‘‘‘ 新鏈接的回調函數 :param server:服務端實例化對象名 :param mask:鏈接數 :return:none ‘‘‘ show("正在監聽[%s]地址[%s]端口,等待連接。。。" % (settings.HOST, settings.PORT), "info") con, addr = server.accept() show("收到{0}的連接請求,正在通信中。。。".format(addr), "info") con.setblocking(False) sel.register(con, selectors.EVENT_READ, server_method) def server_method(con,mask): ‘‘‘ 服務端數據解析主函數 :param con: :param addr: :return: ‘‘‘ # try: cmd = con.recv(1024) # print(cmd) if not cmd: sel.unregister(con) con.close() else: # print(cmd) data = cmd.decode() res = data.split("+") name = res[1] psd = res[2] if res[0] == "login": show("收到客戶端登陸的請求,正在登陸。。。", "msg") user = User(name, psd) sign = user.login() if sign: con.sendall(bytes("Success", encoding="utf-8")) else: con.sendall(bytes("Failure", encoding="utf-8")) elif res[0] == "register": show("收到客戶端註冊的請求,正在註冊。。。", "msg") user = User(name, psd) if user.register(): con.sendall(bytes("Success", encoding="utf-8")) else: con.sendall(bytes("Failure", encoding="utf-8")) elif res[0] == "view": show("收到客戶端查看當前目錄文件的請求。。。", "msg") user = User(name, psd) res = user.view_file() file = str(res) con.sendall(bytes(file, encoding="utf-8")) show("當前目錄文件查看成功", "info") elif res[0] == "upload": show("收到客戶端上傳文件的請求。。。", "msg") con.send(bytes("True", encoding="utf-8")) elif res[0] == "uploadfile": res_length = res[3] filename = res[4] # print(res_length) User.receive(filename, name, res_length, con) elif res[0] == "download": show("收到客戶端下載文件的請求。。。", "msg") user = User(name, psd) res = str(user.view_file()) con.sendall(bytes(res, encoding="utf-8")) elif res[0] == "downloadfile": filename = res[3] show("開始下載文件", "msg") User.download_file(filename, name, con) show("文件下載成功", "info") # except Exception as e: # con.close() # show("發生錯誤:%s"%e,"error") def func(): ‘‘‘ 服務端主函數 :return: none ‘‘‘ server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind((settings.HOST,settings.PORT)) server.listen(100) server.setblocking(False) sel.register(server, selectors.EVENT_READ, accept) while True: events = sel.select() # print(events) #11111111111 for key,mask in events: callback = key.data # print(key.data, "-----") callback(key.fileobj,mask) # print(key.fileobj,"-----",mask)
6、Select FTPServer/src/user.py
import os,sys,pickle,socket,time Base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(Base_dir) from conf import settings from src.common import show class User(object): ‘‘‘ 用戶類 ‘‘‘ def __init__(self,username,psd): self.name = username self.password = psd self.home_path = settings.user_home + "/" +self.name def login(self): ‘‘‘ 用戶登陸方法 :return: ‘‘‘ user_dic = User.info_read(self.name) if user_dic[self.name] == self.password: show("登陸成功","info") return True else: show("登陸失敗,用戶名或密碼錯誤","error") return False def register(self): ‘‘‘ 用戶註冊方法 :return: ‘‘‘ dic = {} dic[self.name] = self.password if User.info_write(self.name,dic): show("註冊成功","info") os.mkdir(self.home_path) os.mkdir("%s/others" % self.home_path) with open("%s\空白文件" % self.home_path, "w") as f: f.write("空白文件") return True else: show("註冊失敗","error") return False def view_file(self): ‘‘‘ 查看當前目錄下文件 :return: 目錄下文件名組成的列表 ‘‘‘ if not os.path.exists(self.home_path): os.mkdir(self.home_path) with open("%s\空白文件"%self.home_path,"w") as f: f.write("空白文件") for root, dirs, files in os.walk(self.home_path): return files @staticmethod def download_file(filename,name,con): ‘‘‘ 下載文件靜態方法 :param filename: 文件名 :param name: 用戶名 :param con: 標誌 :return: none ‘‘‘ dir = os.path.join(os.path.join(os.path.join(Base_dir, "home"), name), filename) with open(dir,"rb") as f: data = f.read() length = str(len(data)) # print(type(a)) con.sendall(bytes(length,encoding="utf-8")) time.sleep(1) # con.recv(1024) # print(con.recv(1024).decode()) con.sendall(data) @staticmethod def receive(filename,name,res_length,con): ‘‘‘ 接收文件靜態方法 :param filename: 文件名 :param name: 用戶名 :param con: 標誌 :return: none ‘‘‘ con.send("True".encode("utf-8")) # print(filename) time.sleep(0.5) dir = os.path.join(os.path.join(os.path.join(Base_dir,"home"),name),filename) length = 0 f = open(dir, "wb") while length < int(res_length): if int(res_length) - length > 1024: size = 1024 else: size = int(res_length) - length data = con.recv(size) length += len(data) # print(length) f.write(data) if length == int(res_length): time.sleep(0.5) show("文件下載成功","info") f.close() return True @staticmethod def info_read(name): ‘‘‘ 讀取用戶數據的靜態方法 :param name: 用戶名 :return: 字典 ‘‘‘ user_dir = os.path.join(settings.user_info,name) if os.path.exists(user_dir): with open(user_dir,"rb") as f: dic = pickle.load(f) return dic else: print("用戶數據為空") @staticmethod def info_write(name,dic): ‘‘‘ 寫入用戶數據的靜態方法 :param name:用戶名 :param dic:用戶信息字典 :return:True ‘‘‘ user_dir = os.path.join(settings.user_info, name) with open(user_dir,"wb") as f: pickle.dump(dic,f) return True #
python之路——作業:Select FTP(僅供參考)