FTP上传下载服务器
要求:
1、多用户认证
2、每个用户有自己的家目录
3、ls 查看当前目录
4、get file 下载文件
5、put File 上传文件
6、del file 删除文件或文件夹
7、mkdir dir 创建文件夹
8、cd 切换目录
9、日志记录
目录结构
import optparse import socket import json,os import shelve class FtpClient(object): """ftp客户端""" MSG_SIZE = 1024 # 消息最长1024 def __init__(self): self.username = None #用户名 self.terminal_display = None self.shelve_obj = shelve.open(".luffy_db") #存放断点续传信息 self.current_dir = None #当前所在路径 parser = optparse.OptionParser() parser.add_option("-s","--server", dest="server", help="ftp server ip_addr") parser.add_option("-P","--port",type="int", dest="port", help="ftp server port") parser.add_option("-u","--username", dest="username", help="username info") parser.add_option("-p","--password", dest="password", help="password info") self.options , self.args = parser.parse_args() #拿到用户输入参数 # print(self.options,self.args,type(self.options),self.options.server) self.argv_verification() self.make_connection() def argv_verification(self): """检查参数合法性""" if not self.options.server or not self.options.port: #IP和端口不能为空 exit("Error: must supply server and port parameters") def make_connection(self): """建立socket链接""" self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建socket对象 self.sock.connect((self.options.server,self.options.port)) #连接服务端 def get_response(self): """获取服务器端返回""" data = self.sock.recv(self.MSG_SIZE) #接受信息 return json.loads(data.decode()) #返回信息 def auth(self): """用户认证""" count = 0 while count < 3: #3次认证 username = input("username:").strip() if not username:continue password = input("password:").strip() cmd = { 'action_type':'auth', 'username':username, 'password':password, } self.sock.send(json.dumps(cmd).encode("utf-8")) #发送服务端进行验证 response = self.get_response() #拿到返回结果 print("response:",response) if response.get('status_code') == 200: #pass auth self.username = username #记录当前用户名 self.terminal_display = "[%s]>>:" % self.username #命令前显示 self.current_dir = "\" #当前路径为根目录 return True else: print(response.get("status_msg")) #打印错误信息 count += 1 def unfinished_file_check(self): """检查shelve db ,把为正常传完的文件列表打印,按用户的指令决定是否重传""" if list(self.shelve_obj.keys()): #判断断点续传信息中是否有信息 print("-------Unfinished file list -------------") for index,abs_file in enumerate(self.shelve_obj.keys()): #拿到索引和文件绝对路径 # del self.shelve_obj[abs_file] received_file_size = os.path.getsize(self.shelve_obj[abs_file][1]) #拿到在本地的文件名,拿到已下载的大小 print("%s. %s %s %s %s" %(index,abs_file, self.shelve_obj[abs_file][0], received_file_size, received_file_size/self.shelve_obj[abs_file][0]*100 )) #打印索引、文件路径、文件大小、已经下载大小,百分比 while True: choice = input("[select file index to re-download]").strip() #等待用输入索引 if not choice:continue if choice == 'back':break #跳过续传 if choice.isdigit(): choice = int(choice) #将选择的数字字符串转化为整形 if choice >= 0 and choice <= index: #判断索引存在 selected_file = list(self.shelve_obj.keys())[choice] #拿到选择续传的文件的文件路径 already_received_size = os.path.getsize(self.shelve_obj[selected_file][1]) #拿到文件大小 print("tell server to resend file ", selected_file) #abs_filename + size +received_size self.send_msg('re_get', file_size=self.shelve_obj[selected_file][0], #发送消息头,包括文件名,大小,已下载大小 received_size=already_received_size, abs_filename=selected_file) response = self.get_response() #拿到返回消息 if response.get('status_code') == 401: #"File exist ,ready to re-send !", local_filename = self.shelve_obj[selected_file][1] #拿到本地已下载的文件名 f = open(local_filename,'ab') #打开文件追加写 total_size = self.shelve_obj[selected_file][0] recv_size = already_received_size current_percent = int(recv_size /total_size *100) progress_generator = self.progress_bar(total_size,current_percent,current_percent) progress_generator.__next__() while recv_size < total_size: if total_size - recv_size < 8192: # last recv data = self.sock.recv(total_size - recv_size) else: data = self.sock.recv(8192) recv_size += len(data) f.write(data) progress_generator.send(recv_size) print("file re-get done") else: print(response.get("status_msg")) #打印错误信息 def interactive(self): """处理与Ftpserver的所有交互""" if self.auth(): #验证成功 self.unfinished_file_check() #进入断点续传检查 while True: user_input = input(self.terminal_display).strip() #输入指令 if not user_input:continue cmd_list = user_input.split() #字符串指令分割 if hasattr(self,"_%s"%cmd_list[0]): #判断类时候有指令方法 func = getattr(self,"_%s"%cmd_list[0]) #拿到指令对应的方法 func(cmd_list[1:]) #传入指令并执行 def parameter_check(self,args,min_args=None,max_args=None,exact_args=None): """参数个数合法性检查""" if min_args: #最少参数个数 if len(args) < min_args: print("must provide at least %s parameters but %s received." %(min_args,len(args))) return False if max_args: #最多参数个数 if len(args) > max_args: print("need at most %s parameters but %s received." %(max_args,len(args))) return False if exact_args: #特定参数个数 if len(args) != exact_args: print("need exactly %s parameters but %s received." % (exact_args, len(args))) return False return True def send_msg(self,action_type,**kwargs ): """打包消息并发送到远程""" msg_data = { 'action_type': action_type, 'fill':'' } #消息头初始内容 msg_data.update(kwargs) #将传入的关键字参数更新到消息头 bytes_msg = json.dumps(msg_data).encode() #序列化并转成bytes if self.MSG_SIZE > len(bytes_msg): #消息头定长 msg_data['fill'] = msg_data['fill'].zfill( self.MSG_SIZE - len(bytes_msg)) bytes_msg = json.dumps(msg_data).encode() self.sock.send(bytes_msg) #发送消息头 def _ls(self,cmd_args): """ display current dir's file list :param cmd_args: :return: """ self.send_msg(action_type='ls') #发送ls的消息头 response = self.get_response() print(response) if response.get('status_code') == 302: #ready to send long msg cmd_result_size = response.get('cmd_result_size') #信息的大小 received_size = 0 cmd_result = b'' while received_size < cmd_result_size: #循环接受消息 if cmd_result_size - received_size < 8192: #last receive data = self.sock.recv( cmd_result_size - received_size) else: data = self.sock.recv(8192) cmd_result += data received_size += len(data) else: print(cmd_result.decode("gbk")) #打印结果 def _cd(self,cmd_args): """change to target dir""" if self.parameter_check(cmd_args, exact_args=1): #只有一个参数 target_dir = cmd_args[0] #拿到目标目录 self.send_msg('cd',target_dir=target_dir) #发送消息头,包括目标目录 response = self.get_response() print(response) if response.get("status_code") == 350:#dir changed self.terminal_display = "[\%s & %s]" % (response.get('current_dir'),self.username) #显示目录和用户名 self.current_dir = response.get('current_dir') #修改当前目录 def _get(self,cmd_args): """download file from ftp server 1.拿到文件名 2.发送到远程 3.等待服务器返回消息 3.1 如果文件存在, 拿到文件大小 3.1.1 循环接收 3.2 文件如果不存在 print status_msg """ if self.parameter_check(cmd_args,min_args=1): filename = cmd_args[0] self.send_msg(action_type='get',filename=filename) #发送get指令和文件名 response = self.get_response() if response.get('status_code') == 301:# file exist ,ready to receive file_size = response.get('file_size') #文件大小 received_size = 0 #已接收大小 progress_generator = self.progress_bar(file_size) #初始化进度条生成器 progress_generator.__next__() #先next才能send #save to shelve db file_abs_path = os.path.join(self.current_dir,filename) #拿到文件在服务器的绝对路径 self.shelve_obj[file_abs_path] = [file_size,"%s.download" %filename] #将文件信息存在本地 f = open("%s.download" %filename,"wb") #打开文件,就收数据 while received_size < file_size: if file_size - received_size < 8192:#last recv data = self.sock.recv( file_size - received_size ) else: data = self.sock.recv(8192) received_size += len(data) f.write(data) #写数据 progress_generator.send(received_size) #打印进度条 else: print(' ') print("---file [%s] recv done,received size [%s]----"%( filename,file_size)) del self.shelve_obj[file_abs_path] #文件传输完毕,删除本地信息 f.close() os.rename("%s.download"%filename,filename) #改文件名 else: print(response.get('status_msg')) #打印错误信息 def progress_bar(self,total_size,current_percent=0,last_percent=0): while True: received_size = yield current_percent #收到当前接受到的文件大小,循环完返回当前的百分比 current_percent = int(received_size / total_size *100) if current_percent > last_percent: print("#" * int(current_percent / 2) + "{percent}%".format(percent=current_percent), end=' ', flush=True) last_percent = current_percent # 把本次循环的percent赋值给last def _put(self,cmd_args): """上传本地文件到服务器 1. 确保本地文件存在 2. 拿到文件名+大小,放到消息头里发给远程 3. 打开文件,发送内容 """ if self.parameter_check(cmd_args, exact_args=1): #检查参数个数 local_file = cmd_args[0] #拿到要上传的文件名 if os.path.isfile(local_file): #判断本地文件是否存在 total_size = os.path.getsize(local_file) #拿到文件大小 self.send_msg('put',file_size=total_size,filename=local_file) #发送put指令,包括文件名、大小 f = open(local_file,'rb') uploaded_size = 0 #last_percent = 0 progress_generator = self.progress_bar(total_size) #进度条 progress_generator.__next__() for line in f: #发送数据 self.sock.send(line) uploaded_size += len(line) progress_generator.send(uploaded_size) #打印进度条 else: print(' ') print('file upload done'.center(50,'-')) f.close() def _mkdir(self,cmd_args): """创建文件夹""" if self.parameter_check(cmd_args, exact_args=1): #检查参数个数 dir_name = cmd_args[0] #拿到要创建的目录 self.send_msg('mkdir', dir_name=dir_name) # 发送mkdir指令,包括目录 response = self.get_response() print(response.get('status_msg')) def _del(self,cmd_args): """创建文件夹""" if self.parameter_check(cmd_args, exact_args=1): #检查参数个数 target = cmd_args[0] #拿到要删除的目录或文件 self.send_msg('del', target=target) # 发送del指令,包括目标 response = self.get_response() print(response.get('status_msg')) if __name__ == "__main__": client = FtpClient() client.interactive() #交互 luffy_client.py
import os,sys #拿到server的绝对路径,添加到搜索模块的路径集 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(BASE_DIR) if __name__ == "__main__": from core import management argv_parser = management.ManagementTool(sys.argv) #将用户输入指令传入管理工具,检验合法性 argv_parser.execute() #指令合法后解析执行 luffy_server.py
[alex] name = alex Li password = e99a18c428cb38d5f260853678922e03 expire = 2017-09-20 [egon] name = egg lin password = e99a18c428cb38d5f260853678922e03 expire = 2018-01-01 accounts.ini
import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) HOST = "0.0.0.0" PORT = 9999 USER_HOME_DIR = os.path.join(BASE_DIR,'home') LOG_DIR=os.path.join(BASE_DIR,'log') ACCOUNT_FILE = "%s/conf/accounts.ini" % BASE_DIR MAX_SOCKET_LISTEN = 5 settings.py
import os import logging.config import time from conf.settings import LOG_DIR # 定义三种日志输出格式 开始 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' '[%(levelname)s][%(message)s]' simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' # id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s' id_simple_format = '%(message)s' # 定义日志输出格式 结束 # logfile_dir = os.path.dirname(os.path.abspath(__file__)) # log文件的目录 logfile_name = '%s.log'%time.strftime('%Y-%m-%d',time.localtime()) # log文件名 # 如果不存在定义的日志目录就创建一个 if not os.path.isdir(LOG_DIR): os.mkdir(LOG_DIR) # log文件的全路径 # logfile_path = os.path.join(logfile_dir, logfile_name) logfile_path = "%s%s%s" % (LOG_DIR, os.path.sep, logfile_name) # log配置字典 LOGGING_DIC = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': id_simple_format }, }, 'filters': {}, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', # 打印到屏幕 'formatter': 'simple' }, 'default': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'filename': logfile_path, # 日志文件 'maxBytes': 1024*1024*5, # 日志大小 5M 'backupCount': 5, 'formatter': 'standard', 'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了 }, }, 'loggers': { '': { 'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕 'level': 'DEBUG', 'propagate': True, # 向上(更高level的logger)传递 }, }, } def load_logging_cfg(): logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置 logger = logging.getLogger(__name__) # 生成一个log实例 # logger.info('log 配置文件没有问题!') # 记录该文件的运行状态 return logger if __name__ == '__main__': load_my_logging_cfg() log_conf.py
import socket from conf import settings import json,hashlib,os,time import configparser import subprocess from core import log_conf class FTPServer(object): """处理与客户端所有的交互的socket server""" STATUS_CODE ={ 200 : "Passed authentication!", 201 : "Wrong username or password!", 300 : "File does not exist !", 301 : "File exist , and this msg include the file size- !", 302 : "This msg include the msg size!", 350 : "Dir changed !", 351 : "Dir doesn't exist !", 401 : "File exist ,ready to re-send !", 402 : "File exist ,but file size doesn't match!", 501 : "directory create success!", 502 : "directory exist!", 503 : "File delete successful!", 504 : "The folder is not empty and cannot be deleted!", } #状态码集合 MSG_SIZE = 1024 #消息最长1024 def __init__(self,management_instance): self.management_instance = management_instance #将ManagementTool对象本身传入 self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #建立socket对象 self.sock.bind((settings.HOST,settings.PORT)) #绑定IP和端口 self.sock.listen(settings.MAX_SOCKET_LISTEN) #开始监听 self.accounts = self.load_accounts() #加载账户信息 self.user_obj = None #保存当前用户 self.user_current_dir = None #当前用户目录 self.log=log_conf.load_logging_cfg() def run_forever(self): """启动socket server""" print('starting LuffyFtp server on %s:%s'.center(50,'-') %(settings.HOST,settings.PORT)) while True: #循环接受连接 self.request,self.addr = self.sock.accept() #拿到链接和地址 # print("got a new connection from %s....." %(self.addr,)) self.log.info("got a new connection from %s....." %(self.addr,)) try: self.handle() #进入用户交互 except Exception as e: # print("Error happend with client,close connection.",e) self.log.info("Error happend with client,close connection.",e) self.request.close() #打印错误信息,关闭连接 def handle(self): """处理与用户的所有指令交互""" while True: raw_data = self.request.recv(self.MSG_SIZE) #接受客户端发送的消息 print('------->',raw_data) if not raw_data: #用户断了连接,打印消息并删除链接和地址 # print("connection %s is lost ...." % (self.addr,)) self.log.info("connection %s is lost ...." % (self.addr,)) del self.request,self.addr break data = json.loads(raw_data.decode("utf-8")) #先转化为utf—8再反序列化 action_type = data.get('action_type') #拿到指令 if action_type: if hasattr(self,"_%s" % action_type): #查找类是否有对应方法 func = getattr(self,"_%s" % action_type) #拿到对应方法并执行 func(data) else: print("invalid command,") def load_accounts(self): """加载所有账号信息""" config_obj = configparser.ConfigParser() #拿到信息对象 config_obj.read(settings.ACCOUNT_FILE) #读取本地信息 print(config_obj.sections()) #打印信息标头 return config_obj def authenticate(self,username,password): """用户认证方法""" if username in self.accounts: #判断用户是否存在 _password = self.accounts[username]['password'] #拿到用户密码 md5_obj = hashlib.md5() md5_obj.update(password.encode()) md5_password = md5_obj.hexdigest() #拿到密码的MD5值 # print("passwd:",_password,md5_password) if md5_password == _password: #判断密码是否相等 self.user_obj = self.accounts[username] #保存用户信息 self.user_obj['home']= os.path.join(settings.USER_HOME_DIR,username) #服务端用户家目录的的绝对路径 #set user home directory self.user_current_dir = self.user_obj['home'] #保存当前路径 return True #认证成功返回TRUE else: return False #密码错误返回FALSE else: return False #用户名错误返回FALSE def send_response(self,status_code,*args,**kwargs): """ 打包发送消息给客户端 :param status_code: :param args: :param kwargs: {filename:ddd,filesize:222} :return: """ data = kwargs data['status_code'] = status_code #返回状态码 data['status_msg'] = self.STATUS_CODE[status_code] #返回信息 data['fill'] = '' bytes_data = json.dumps(data).encode() #序列化并转bytes if len(bytes_data) < self.MSG_SIZE: #定长 data['fill'] = data['fill'].zfill( self.MSG_SIZE - len(bytes_data)) bytes_data = json.dumps(data).encode() self.request.send(bytes_data) #发送到客户端 def _auth(self,data): """处理用户认证请求""" # print("auth ",data ) if self.authenticate(data.get('username'),data.get('password')): #进行认证 print('pass auth....') #1. 消息内容,状态码 #2. json.dumps #3 . encode self.send_response(status_code=200) #认证成功返回200 else: self.send_response(status_code=201) #认证失败返回201 def _get(self,data): """client downloads file through this method 1. 拿到文件名 2. 判断文件是否存在 2.1 如果存在, 返回状态码+文件大小 2.1.1打开文件,发送文件内容 2.2 如果不存在, 返回状态码 3. """ filename = data.get('filename') #拿到文件名 #full_path = os.path.join(self.user_obj['home'],filename) full_path = os.path.join(self.user_current_dir,filename) #拿到文件路径 if os.path.isfile(full_path): #判断文件是否存在 filesize = os.stat(full_path).st_size #拿到文件大小 self.send_response(301,file_size=filesize) #返回状态码和文件大小 print("ready to send file ") f = open(full_path,'rb') #打开文件,发送数据 for line in f: self.request.send(line) else: # print('file send done..',full_path) self.log.info('file send done..',full_path) f.close() else: self.send_response(300) #文件不存在,返回状态码 def _re_get(self,data): """re-send file to client 1. 拼接文件路径 2. 判断文件是否存在 2.1 如果存在,判断文件大小是否与客户端发过来的一致 2.1.1 如果不一致,返回错误消息 2.1.2 如果一致,告诉客户端,准备续传吧 2.1.3 打开文件,Seek到指定位置,循环发送 2.2 文件不存在,返回错误 """ print("_re_get",data) abs_filename = data.get('abs_filename') #拿到文件路径 full_path = os.path.join(self.user_obj['home'],abs_filename.strip("\")) #拼接家目录和文件名 print("reget fullpath", full_path) # print("user home",self.user_obj['home']) if os.path.isfile(full_path): #判断文件是否存在 if os.path.getsize(full_path) == data.get('file_size'): #判断文件大是否一致 self.send_response(401) #返回状态码,让客户端准备接收数据 f = open(full_path,'rb') #打开文件 f.seek(data.get("received_size")) #光标移动到断点处 for line in f: self.request.send(line) else: # print("-----file re-send done------") self.log.info('file re-send done..', full_path) f.close() else:#2.1.1 self.send_response(402,file_size_on_server=os.path.getsize(full_path)) #文件大小不一致,返回错误信息 else: self.send_response(300) #文件不存在,返回错误信息 def _put(self,data): """client uploads file to server 1. 拿到local文件名+大小 2. 检查本地是否已经有相应的文件。self.user_cuurent_dir/local_file 2.1 if file exist , create a new file with file.timestamp suffix. 2.2 if not , create a new file named local_file name 3. start to receive data """ local_file = data.get("filename") #拿到文件名 full_path = os.path.join(self.user_current_dir,local_file) #文件路径 if os.path.isfile(full_path): #代表文件已存在,不能覆盖 filename = "%s.%s" %(full_path,time.time()) #保存新的文件名 else: filename = full_path f = open(filename,"wb") #打开文件,准备写数据 total_size = data.get('file_size') received_size = 0 while received_size < total_size: if total_size - received_size < 8192: # last recv data = self.request.recv(total_size - received_size) else: data = self.request.recv(8192) received_size += len(data) f.write(data) print(received_size, total_size) else: # print('file %s recv done'% local_file) self.log.info('file %s recv done'% local_file) f.close() def _ls(self,data): """run dir command and send result to client""" cmd_obj = subprocess.Popen('dir %s' %self.user_current_dir,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) #执行Windows上的dir命令 stdout = cmd_obj.stdout.read() #拿到执行结果 stderr = cmd_obj.stderr.read() #拿到错误信息 cmd_result = stdout + stderr #拼接结果信息 if not cmd_result: #没有结果返回结果,定义返回结果 cmd_result = b'current dir has no file at all.' self.send_response(302,cmd_result_size=len(cmd_result)) #发送状态码和信息长度 self.request.sendall(cmd_result) #发送结果 def _cd(self,data): """根据用户的target_dir改变self.user_current_dir 的值 1. 把target_dir 跟user_current_dir 拼接 2. 检测 要切换的目录是否存在 2.1 如果存在 , 改变self.user_current_dir的值到新路径 2.2 如果不存在,返回错误消息 """ #/home/alex/FuckHomework/ cfd target_dir = data.get('target_dir') #拿到客户端发送来的目标路径 full_path = os.path.abspath(os.path.join(self.user_current_dir,target_dir) ) #拼接当前路径和目标路径,再拿到服务端的绝对路径 print("full path:",full_path) if os.path.isdir(full_path): #判断路径是否存在 if full_path.startswith(self.user_obj['home']): #判断路径是否是用户家目录下的路径 self.user_current_dir = full_path #是的话,将目标路径赋值给当前路径 relative_current_dir = self.user_current_dir.replace(self.user_obj['home'], '') #用户拿到相对路径 self.send_response(350, current_dir=relative_current_dir) #发送给用户状态码和相对路径 else: self.send_response(351) #目标路径不是用户允许访问的路径,返回状态码 else: self.send_response(351) #目标路径不存在,返回状态码 def _mkdir(self,data): """ 创建文件夹目录 1、将用户发送的dir_name和self.user_current_dir进行拼接 2、判断文件夹是否存在 2.1不存在就创建 并返回信息 2.2存在就返回错误信息 :param data: :return: """ dir_name = data.get('dir_name') #拿到目录名字 full_path = os.path.abspath(os.path.join(self.user_current_dir,dir_name)) #拼接路径 if not os.path.isdir(full_path): #判断目录是否存在 os.makedirs(full_path) #不存在则创建 self.log.info('%s directory create success!' % full_path) #日志记录并打印 self.send_response(501) #返回状态码 else: self.send_response(502) def _del(self,data): """ 创建文件夹目录 1、将用户发送的target和self.user_current_dir进行拼接 2、判断文件或者文件夹是否存在 2.1文件存在,删除文件 2.2文件夹存在,判断是否为空 2.2.1空文件夹可删除 2.2.2文件夹不为空,不可删除 2.3路径不存在,返回错误信息 :param data: :return: """ target = data.get('target') full_path = os.path.abspath(os.path.join(self.user_current_dir,target)) #拼接路径 if os.path.isfile(full_path): #判断是否为文件 os.remove(full_path) #删除文件 self.log.info('%s file delete success!' % full_path) self.send_response(503) elif os.path.isdir(full_path): #判断路径为文件夹 if not os.listdir(full_path): #文件夹为空,可以删除 os.rmdir(full_path) self.log.info('%s directory delete success!' % full_path) self.send_response(503) else: self.send_response(504) #不为空,不能删除,并发送结果 else: self.send_response(300) #文件不存在 main.py
from core import main class ManagementTool(object): """负责对用户输入的指令进行解析并调用相应模块处理""" def __init__(self,sys_argv): self.sys_argv = sys_argv #接受用户输入指令 print(self.sys_argv) self.verify_argv() #检验指令合法性 def verify_argv(self): """验证指令合法性""" if len(self.sys_argv) < 2: #至少输入一个参数 self.help_msg() #输出帮助信息 cmd = self.sys_argv[1] #拿到指令 if not hasattr(self,cmd): #判断类是否存在对应方法 print("invalid argument!") self.help_msg() def help_msg(self): """帮助信息""" msg = ''' start start FTP server stop stop FTP server restart restart FTP server createuser username create a ftp user ''' exit(msg) def execute(self): """解析并执行指令""" cmd = self.sys_argv[1] func = getattr(self,cmd ) func() def start(self): """start ftp server""" server = main.FTPServer(self) #建立socket对象 server.run_forever() #启动 def creteuser(self): """ 创建用户 :return: """ print(self.sys_argv) management.py
[2017-07-17 16:48:35,798][MainThread:1952][task_id:core.log_conf][main.py:47][INFO][got a new connection from ('127.0.0.1', 49629).....] [2017-07-17 16:49:14,426][MainThread:1952][task_id:core.log_conf][main.py:294][INFO][E:PycharmProjectsqz5Day28LuffyFTPserverhomealexabcabcd directory create success!] [2017-07-17 16:49:42,910][MainThread:1952][task_id:core.log_conf][main.py:320][INFO][E:PycharmProjectsqz5Day28LuffyFTPserverhomealexabcabcd directory delete success!] [2017-07-17 16:49:56,433][MainThread:1952][task_id:core.log_conf][main.py:320][INFO][E:PycharmProjectsqz5Day28LuffyFTPserverhomealexabcab directory delete success!] [2017-07-17 16:50:00,880][MainThread:1952][task_id:core.log_conf][main.py:64][INFO][connection ('127.0.0.1', 49629) is lost ....] 2017-07-17.log