Python socket编程-进阶5
基于socket搭建的 FTP文件上传功能模块
1. 用户账号密码验证功能
2. 断点续传功能
3. 文件目录切换功能
4. MD5加密功能
使用方法
1. 服务端Terminal 输入 python ftp_server.py start 启动服务端
2. 客户端使用 Terminal 输入 python ftp_client.py -s localhost -p 8081 -u liang -w 123
3. 客户端输入 put 123.png images ( put 为上传方法, 123.png 为客户端下的文件, images为上传到服务端指定的文件夹 )
项目结构
FTP_client
import socket import optparse import json,os,sys class ClientHandler: def __init__(self): """获取 Terminal 指令参数""" 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('-w', '--password', dest='password') self.options, self.args = self.op.parse_args() self.mainpath=os.path.dirname(os.path.abspath(__file__)) self.verify_args() self.make_connection() def verify_args(self): """解析从 Terminal 获取的指令参数""" server = self.options.server port = int(self.options.port) username = self.options.username password = self.options.password if 0 < port <= 65535: return True else: exit('端口号必须介于 0~65535之间') def make_connection(self): """获取socket连接""" self.tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.tcp.connect((self.options.server, int(self.options.port),)) def interactive(self): """通讯""" flag=self.auth() if flag: while True: cmdinfo=input('%s >>>'%self.curdir).strip() cmdlist=cmdinfo.split() if cmdlist[0]: if hasattr(self,cmdlist[0]): func=getattr(self,cmdlist[0]) func(cmdlist) else: print('指令不正确') else: pass def auth(self): """验证""" username = self.options.username password = self.options.password if username is None or password is None: username = input('username:') password = input('password:') data = { 'action': 'auth', 'username': username, 'password': password, } self.tcp.send(json.dumps(data).encode('GBK')) data = self.response() if data['state_code'] ==200: print(data) self.user=username self.curdir=username return True else: print(data) return False def put(self,cmdlist): action,localpath,targetpath=cmdlist localpath=os.path.join(self.mainpath,localpath) filename=os.path.basename(localpath) filesize=os.stat(localpath).st_size data={ 'action':action, 'targetpath':targetpath, 'filename':filename, 'filesize':filesize } self.tcp.sendall(json.dumps(data).encode('utf-8')) data=self.response() if data['state_code'] ==800: # 文件不存在,可以上传! self.read(localpath,filesize,'rb') elif data['state_code'] ==801: # 断点续传 choices=input('do you want continue? [Y/N]:') if choices.upper()=='Y': self.tcp.sendall('Y'.encode('utf-8')) position=self.tcp.recv(1024).decode('utf-8') has_send=int(position) self.read(localpath,filesize,'rb',has_send=has_send) elif choices.upper() =='N': self.tcp.sendall('N'.encode('utf-8')) elif data['state_code'] ==802: #文件已经存在 print(data) return def read(self,filename,filesize,mode,has_send=0): f=open(filename,mode) f.seek(has_send) while has_send<filesize: data=f.read(200) self.tcp.sendall(data) has_send+=len(data) self.show_progress(has_send,filesize) f.close() print('upload finish') def show_progress(self,has_send,filesize): rate=float(has_send)/float(filesize) rate=int(rate*100) sys.stdout.write('%s%% %s\r'%(rate,'*'*rate)) def ls(self,cmdlist): action=cmdlist[0] data = { 'action': action, } self.tcp.sendall(json.dumps(data).encode('utf-8')) data = self.tcp.recv(1024).decode('utf-8') print(data) def cd(self,cmdlist): action,dirname=cmdlist data = { 'action': action, 'dirname':dirname } self.tcp.sendall(json.dumps(data).encode('utf-8')) data = self.tcp.recv(1024).decode('utf-8') self.curdir=os.path.basename(data) def makedir(self,cmdlist): action,dirname=cmdlist data = { 'action': action, 'dirname':dirname } self.tcp.sendall(json.dumps(data).encode('utf-8')) data = self.tcp.recv(1024).decode('utf-8') print(data) def response(self): data = self.tcp.recv(1024).decode('GBK') data = json.loads(data) return data client = ClientHandler() client.interactive()
FTP_server
1. bean\ ftp_server.py
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()
2. conf\ settings.py
IP='localhost' PORT=8081 import os BASE_PATH=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) USERS_PATH=os.path.join(BASE_PATH,'conf','users.txt')
3. core\ main.py
import optparse import socketserver import os,sys PATH= os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(PATH) from conf import settings from core.server import ServerHandler class ArgvHandler: def __init__(self): self.op = optparse.OptionParser() options, args = self.op.parse_args() self.verify_args(options, args) def verify_args(self, options, args): cmd = args[0] if hasattr(self, cmd): func = getattr(self, cmd) func() def start(self): tcp = socketserver.ThreadingTCPServer((settings.IP,settings.PORT,), ServerHandler) tcp.serve_forever()
4. core\ server.py
import socketserver import json, configparser from conf import settings import os class ServerHandler(socketserver.BaseRequestHandler): def handle(self): while True: try: data = self.request.recv(1024).strip() data = json.loads(data.decode('GBK')) if hasattr(self, data.get('action')): func = getattr(self, data.get('action')) func(**data) else: print('Invalid ,not found auth method !') except Exception as e: print(e) break def auth(self, **data): username = data['username'] password = data['password'] conf = configparser.ConfigParser() conf.read(settings.USERS_PATH) if username in conf.sections(): if conf[username]['Password'] == password: self.user=username self.mainpath=os.path.join(settings.BASE_PATH,'media',self.user) self.send_response(200,'successful') else: self.send_response(500,'password is error !') else: self.send_response(500,'username is not exit !') def put(self,**data): filename=data['filename'] filesize=data['filesize'] targetpath=data['targetpath'] abs_path=os.path.join(self.mainpath,targetpath,filename) if os.path.exists(abs_path): has_size=os.stat(abs_path).st_size if has_size<filesize: self.send_response(801,'断点续传!') choices=self.request.recv(1024).decode('utf-8') if choices =='Y': self.request.sendall(str(has_size).encode('utf-8')) self.write(abs_path,filesize,'ab',has_size=has_size) elif choices=='N': # 拒绝断点续传重新上传 self.write(abs_path,filesize,'wb') else: self.send_response(802,'文件已经存在!') return else: self.send_response(800,'文件不存在,可以上传!') self.write(abs_path,filesize,'wb') def write(self,filename,filesize,mode,has_size=0): f=open(filename,mode) while has_size<filesize: data=self.request.recv(200) f.write(data) has_size+=len(data) f.close() def ls(self,**data): file_list=os.listdir(self.mainpath) file_list='\n'.join(file_list) if len(file_list)==0: file_list='empty folder!' self.request.sendall(file_list.encode('utf-8')) def cd(self,**data): dirname=data['dirname'] if dirname =='..': self.mainpath=os.path.dirname(self.mainpath) else: self.mainpath=os.path.join(self.mainpath,dirname) self.request.sendall(self.mainpath.encode('utf-8')) def makedir(self,**data): dirname=data['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('文件创建成功'.encode('utf-8')) else: self.request.sendall('文件已经存在'.encode('utf-8')) def send_response(self,state_code,msg): response={'state_code':state_code,'msg':msg} self.request.sendall(json.dumps(response).encode('GBK'))