功能:
1.用户加密认证;
2.允许同时多用户登陆;
3.每个用户有自己的家目录,并且只能访问在自己的家目录;
4.对用户进行磁盘配额,每个用户的可用空间不同;
5.允许用户在ftp server上随意切换目录;
6.允许用户查看当前目录上下文;
7.允许用户上传和下载文件,保证文件的一致性
8.文件传输过程中显示进度条;
客户端:
# Author:q1.ang import socket,os,json import hashlib import sys class FtpClient(object): def __init__(self): self.client = socket.socket() self.account=‘‘ self.path=‘‘ def help(self): ‘‘‘ 帮助 :return: none ‘‘‘ msg= ‘‘‘help: dir cd ../.. new .. del .. re .. put .. get ..‘‘‘ print(msg) def connect(self,ip,port): ‘‘‘ 连接 :param ip: :param port: :return: ‘‘‘ self.client.connect((ip,port)) def authenticate(self): ‘‘‘ 用户登陆认证 :return:auth真假 ‘‘‘ auth=False try_count=0 while try_count<3: self.account=input(‘Account:‘).strip() password=input(‘Password:‘).strip() #获取账号密码的MD5值 m=hashlib.md5() m.update((self.account+password).encode(‘utf-8‘)) #发送acc+MD5 self.client.send((self.account+‘ ‘+m.hexdigest()).encode(‘utf-8‘)) #接收服务器的认证结果 auth_recv=json.loads(self.client.recv(1024).decode()) print(auth_recv[‘log res‘]) if auth_recv[‘log res‘]==‘login success‘: auth=True print(‘desk size:‘,auth_recv[‘desk size‘]) return auth try_count+=1 if try_count==3: print(‘you have try 3 times...exit‘) else: return auth def interactive(self): ‘‘‘ 交互模块,调用各个功能,在子功能中与服务器实现收发 :return:none ‘‘‘ if self.authenticate(): #认证 while True: cmd = input(‘>>>‘).strip() if len(cmd)==0:continue cmd_str=cmd.split()[0] if hasattr(self,‘cmd_%s‘%cmd_str): func=getattr(self,‘cmd_%s‘%cmd_str) func(cmd) else: self.help() def cmd_dir(self,*args): ‘‘‘ 查看当面目录下文件 :param args: dir没使用 :return: none ‘‘‘ msg_dic = { ‘action‘:‘dir‘ } self.client.send(json.dumps(msg_dic).encode(‘utf-8‘)) print(self.account,‘list:‘) server_respone = self.client.recv(1024).decode() for i in json.loads(server_respone): print(i) def cmd_cd(self,*args): ‘‘‘ 切换目录 :param args: cd 目录名 :return: none ‘‘‘ cmd_split=args[0].split() if len(cmd_split)>1: if cmd_split[1][0:len(self.account)]==self.account: self.path = ‘\\‘+cmd_split[1][len(self.account)+1:] else: self.path += ‘\\‘+cmd_split[1] msg_dic = { ‘action‘: ‘cd‘, ‘path‘:self.path } print(msg_dic) self.client.send(json.dumps(msg_dic).encode(‘utf-8‘)) auth_path=self.client.recv(1024).decode() if auth_path== ‘True‘: print((self.account+‘:‘+self.path).replace(‘\\\\‘,‘\\‘)) else: print(‘找不到指定的路径‘) else: print(‘you not write the folder name...‘) def cmd_new(self,*args): ‘‘‘ 新建目录 :param args: cmd :return: new 目录名 ‘‘‘ cmd_split=args[0].split() if len(cmd_split)>1: foldername=cmd_split[1] msg_dic={ ‘action‘:‘new‘, ‘foldername‘:foldername } self.client.send(json.dumps(msg_dic).encode(‘utf-8‘)) recv=self.client.recv(1024).decode() print(recv) else: print(‘no name of the new file...‘) def cmd_del(self,*args): ‘‘‘ 删除目录或文件 :param args:del 文件名 :return: none ‘‘‘ cmd_split=args[0].split() if len(cmd_split)>1: msg_dic={ ‘action‘:‘del‘, ‘name‘:cmd_split[1] } self.client.send(json.dumps(msg_dic).encode(‘utf-8‘)) recv=self.client.recv(1024).decode() print(recv) else: print(‘no name of the file...‘) def cmd_re(self,*args): ‘‘‘ 重命名 :param args: re 文件名 :return: none ‘‘‘ cmd_split=args[0].split() if len(cmd_split)>1: new_name=input(‘new name:‘).strip() msg_dic={ ‘action‘:‘re‘, ‘name‘:cmd_split[1], ‘new name‘:new_name } self.client.send(json.dumps(msg_dic).encode(‘utf-8‘)) recv=self.client.recv(1024).decode() print(recv) else: print(‘the name of file or folder has not write..‘) def cmd_put(self,*args): ‘‘‘ 上传文件 :param args: put 文件名 :return: none ‘‘‘ cmd_split=args[0].split() if len(cmd_split)>1: filename=cmd_split[1] if os.path.isfile(filename): filesize=os.stat(filename).st_size msg_dic = { ‘action‘: ‘put‘, ‘filename‘: filename, ‘filesize‘: filesize, ‘override‘: True } self.client.send(json.dumps(msg_dic).encode(‘utf-8‘)) server_respone=self.client.recv(1024).decode() if server_respone==‘out of memory‘: print(‘the mamory of your desk is not enough to save this file[%s(%d Mb)]‘ % (filename, filesize / 1048576)) else: l=0 sys.stdout.write(‘0%‘) sys.stdout.flush() with open(filename,‘rb‘) as f: # f=open(filename,‘rb‘) for line in f: self.client.send(line) l+=len(line) if l>(filesize/20): sys.stdout.write(‘>‘) sys.stdout.flush() l-=(filesize/20) else: print(‘100%\nfile upload success...‘) save_sigle=self.client.recv(1024).decode() print(save_sigle) else: print(filename,‘is not exit‘) def cmd_get(self,*args): ‘‘‘ 下载文件 :param args: get 文件名 :return: none ‘‘‘ print(‘get‘) cmd_split=args[0].split() if len(cmd_split)>1: filename=cmd_split[1] msg_dic={ ‘action‘: ‘get‘, ‘filename‘:filename } self.client.send(json.dumps(msg_dic).encode(‘utf-8‘)) auth_recv=json.loads(self.client.recv(1024).decode()) if auth_recv[‘file acc‘]==‘file exist‘: filesize=auth_recv[‘filesize‘] revc_size=0 l=0 sys.stdout.write(‘0%‘) sys.stdout.flush() self.client.send(b‘200‘) #防粘包 if os.path.isfile(filename): filename=filename+‘.new‘ with open(filename,‘wb‘)as f: while revc_size<filesize: recv=self.client.recv(1024) f.write(recv) revc_size+=len(recv) l+=len(recv) if l>(filesize/20): sys.stdout.write(‘>‘) sys.stdout.flush() l-=(filesize/20) else: print(‘100%\nreceive done‘) else: print(auth_recv[‘file acc‘]) else: print(‘error:not write filename...‘) ftp=FtpClient() ftp.connect(‘localhost‘,6666) ftp.interactive()
服务器端:
# Author:q1.ang import socketserver,json,os import hashlib class MyTCPHandler(socketserver.BaseRequestHandler): acc_path = ‘‘ account =‘‘ available_size=0 data_dic={} def authenticate(self): ‘‘‘ 用户认证 :return: auth真假 ‘‘‘ auth=False try_count = 0 while try_count < 3: #接收客户端的账号密码 account,recv_md5=self.request.recv(1024).decode().split() print(account,recv_md5) BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) file_path=‘%s\conf\\accounts\%s‘%(BASE_DIR,account) if os.path.isfile(file_path): with open(file_path,‘r‘) as f: # acc_data=json.load(f) self.data_dic=json.load(f) m=hashlib.md5() m.update((self.data_dic[‘account‘]+self.data_dic[‘password‘]).encode(‘utf-8‘)) print(m.hexdigest()) if m.hexdigest()==recv_md5: print(‘login success‘) self.account=account used_size = int(self.data_dic[‘desk size‘].split(‘/‘)[0]) all_size = int(self.data_dic[‘desk size‘].split(‘/‘)[1]) self.available_size = all_size - used_size print(‘available_size‘,self.available_size) msg_dic = { ‘log res‘: ‘login success‘, ‘desk size‘: ‘%dMb/%dMb‘%(used_size/1048576,all_size/1048576) } auth=True try_count=3 else: print(‘account or password error‘) msg_dic={ ‘log res‘:‘account or password error‘, } else: print(‘the account dose not exist..‘) msg_dic = { ‘log res‘: ‘the account dose not exist..‘, } try_count+=1 self.request.send(json.dumps(msg_dic).encode(‘utf-8‘)) return auth def cmd_dir(self,*args): ‘‘‘ 查看目录下文件 :param args: cmd_dic :return: none ‘‘‘ print(self.acc_path) print(os.listdir(self.acc_path)) self.request.send(json.dumps(os.listdir(self.acc_path)).encode(‘utf-8‘)) def cmd_cd(self,*args): ‘‘‘ 切换目录 :param args: cmd_dic :return: none ‘‘‘ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) self.acc_path = ‘%s\\conf\\data\\%s‘%(BASE_DIR,self.account) auth_path=‘False_path‘ #不能发空字节 path=self.acc_path+args[0][‘path‘] print(‘地址:‘,path) if os.path.exists(path): self.acc_path=path auth_path=‘True‘ print(‘path‘,self.acc_path) else: print(‘找不到指定的路径‘) self.request.send(auth_path.encode(‘utf-8‘)) def cmd_new(self,*args): ‘‘‘ 在当前目录下新建目录 :param args: cmd_dic :return: none ‘‘‘ path=self.acc_path+‘\\‘+args[0][‘foldername‘] if os.path.exists(path): print(‘folder is exist..‘) msg=‘folder is exist..‘ else: os.mkdir(path) print(‘创建成功‘) msg = ‘folder create success..‘ self.request.send(msg.encode(‘utf-8‘)) def cmd_re(self,*args): ‘‘‘ 在当前目录下重命名文件 :param args: cmd_dic :return: none ‘‘‘ name = self.acc_path + ‘\\‘ + args[0][‘name‘] new_name=self.acc_path + ‘\\‘ + args[0][‘new name‘] try: os.rename(name,new_name) msg=‘rename success‘ except FileNotFoundError as e: msg=‘the folder is not exist...‘ print(e) self.request.send(msg.encode(‘utf-8‘)) def cmd_del(self,*args): ‘‘‘ 在当前目录下删除目录(空)或文件 :param args: cmd_dic :return: none ‘‘‘ path = self.acc_path + ‘\\‘ + args[0][‘name‘] if os.path.isfile(path): os.remove(path) msg=‘del success‘ elif os.path.exists(path): try: os.rmdir(path) msg = ‘del success‘ except OSError as e: print(e) msg=‘the folder is not empty...‘ else: msg=‘file or folder is not exist...‘ self.request.send(msg.encode(‘utf-8‘)) def cmd_put(self,*args): ‘‘‘ 接收客户端文件 :return: ‘‘‘ cmd_dic=args[0] filename=cmd_dic[‘filename‘] filesize=cmd_dic[‘filesize‘] if filesize>self.available_size: self.request.send(‘out of memory‘.encode(‘utf-8‘)) else: file_path=‘%s\\%s‘%(self.acc_path,filename) if os.path.isfile(file_path): f=open(file_path+‘.new‘,‘wb‘) else: f = open(file_path, ‘wb‘) self.request.send(b‘200 ok‘) #防粘包 recv_size=0 while recv_size< filesize: data=self.request.recv(1024) f.write(data) recv_size+=len(data) else: f.close() print(‘file [%s] has uploaded‘%filename) self.available_size -= filesize print(‘available_size2‘,self.available_size) self.request.send((‘server save success\navailable size:‘ ‘%d Mb‘%(self.available_size/1048576)).encode(‘utf-8‘)) #更新用户数据 used_size=self.data_dic[‘desk size‘].split(‘/‘)[0] all_size=self.data_dic[‘desk size‘].split(‘/‘)[1] print(‘filesize‘,filesize) print(‘used_size‘,used_size) print(‘all_size‘,all_size) self.data_dic[‘desk size‘]=‘%d/%s‘%(int(used_size)+filesize,all_size) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) file_path = ‘%s\conf\\accounts\%s‘ % (BASE_DIR, self.account) print(file_path) with open(file_path,‘w‘) as f: json.dump(self.data_dic,f) def cmd_get(self,*args): ‘‘‘ 发送给客户端文件 :param args: cmd_dic :return: none ‘‘‘ cmd_dic=args[0] filename=cmd_dic[‘filename‘] file_path=‘%s\\%s‘%(self.acc_path,filename) if os.path.isfile(file_path): filesize=os.stat(file_path).st_size msg_dic={ ‘file acc‘:‘file exist‘, ‘filesize‘:filesize } self.request.send(json.dumps(msg_dic).encode(‘utf-8‘)) self.request.recv(1024) #防粘包 with open(file_path,‘rb‘)as f: for line in f: self.request.send(line) else: print(‘file send done‘) else: msg_dic = {‘file acc‘: ‘file[%s] is not exist...‘%filename} self.request.send(json.loads(msg_dic).encode(‘utf-8‘)) def handle(self): ‘‘‘ 重构socketserver的handle(),用来主要的交互 :return: ‘‘‘ print(‘waiting...‘) if self.authenticate(): BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) self.acc_path = ‘%s\\conf\\data\\%s‘ % (BASE_DIR, self.account) while True: print(‘登录成功‘,self.account) try: self.data=self.request.recv(1024).strip() print(‘{} wrote‘.format(self.client_address[0])) cmd_dic=json.loads(self.data.decode()) print(cmd_dic) action=‘cmd_‘+cmd_dic[‘action‘] if hasattr(self,action): func=getattr(self,action) func(cmd_dic) except ConnectionResetError as e: print(‘ConnectionResetError‘,e) break if __name__==‘__main__‘: HOST,PORT=‘localhost‘,6666 server=socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler) server.serve_forever()
原文地址:https://www.cnblogs.com/q1ang/p/9193232.html
时间: 2024-11-05 16:14:23