def handle(self): """ 服务端处理主方法 self.request 是客户端的socket对象 :return: """ print(self.client_address) status = self.request.recv(8096) # 接收客户端的操作 if status == b"2003": self.request.close() return # 从字典中取出方法 func_str = settings.status.get(status.decode('utf8')) td = TransferDic(self.request) # 接收客户端的信息 user_info = td.accept_packget_dict() action = Action() if hasattr(action, func_str): func = getattr(action, func_str) user_info = func(self.request, user_info) if user_info: # 登陆成功之后的操作 tcp_server = FtpServer(self.request, user_info) tcp_server.run()
def login(self, socket, user_info): """登录相关""" username = user_info['username'] password = user_info['password'] # 读取userinfo文件,查看用户和密码是否正确 td = TransferDic(socket) for user in HandleDb.read_file(settings.user_path): userinfo_list = user.split("|") user = userinfo_list[0].strip() passwd = userinfo_list[1].strip() disk_quota = userinfo_list[2].strip() passwd_md5 = self.ep.md5_encry(password) if username == user and passwd_md5 == passwd: # 如果已经登录成功就返回code td.send_packget_dict({"user_code": "3003"}) return (username, disk_quota) else: td.send_packget_dict({"user_code": "3004"}) return
def register(self, socket, user_info): """注册相关""" username = user_info['username'] password = user_info['password'] # 读取userinfo文件,查看用户是否已经被注册 td = TransferDic(socket) for user in HandleDb.read_file(settings.user_path): userinfo_list = user.split("|") if username == userinfo_list[0].strip(): # 如果已经注册了就返回code td.send_packget_dict({"user_code": "3001"}) return else: passwd = self.ep.md5_encry(password) user_msg = "%s|%s|%s" % (username, passwd, settings.disk_quotas) # 将用户信息写入文件 HandleDb.write_file(settings.user_path, user_msg) # 帮用户以用户名创建空文件夹 userfile_path = os.path.join(settings.home_path, username) os.mkdir(userfile_path) td.send_packget_dict({"user_code": "3002"}) return
def __init__(self, socket, user_info): self.request = socket self.username = user_info[0] self.disk_quota = int(user_info[1]) * 1024 * 1024 self.td = TransferDic(self.request) self.user_dir = os.path.join(settings.home_path, self.username)
class FtpServer(): """文件上传下载服务端""" address_family = socket.AF_INET socket_type = socket.SOCK_STREAM # tcp流类型 allow_reuse_address = False max_packet_size = 8192 coding = 'utf-8' request_queue_size = 5 # 队列最长数量 def __init__(self, socket, user_info): self.request = socket self.username = user_info[0] self.disk_quota = int(user_info[1]) * 1024 * 1024 self.td = TransferDic(self.request) self.user_dir = os.path.join(settings.home_path, self.username) def server_close(self): self.request.close() def close_request(self, request): request.close() def run(self): """服务端处理主方法""" while True: try: head_dic = self.td.accept_packget_dict() if not head_dic: return action = head_dic['action'] if hasattr(self, action): func = getattr(self, action) func(head_dic) except Exception: return def file_md5_pro(self, file_path): """ 计算文件的md5值并返回 :param file_path:文件路径 :return: """ md5 = hashlib.md5() with open(file_path, "rb") as f: for line in f: md5.update(line) return md5.hexdigest() def progress_bar(self, new_size, sum_size): """根据每次输入的大小判断目前的百分比""" bar = int(new_size / sum_size * 100) bar_num = bar // 3 print("\r%s|进度为:%s%%" % ("#" * bar_num, bar), end="") def send_file(self, file_seek, filename, filesize): send_size = file_seek # 最后发送需要传输的文件 with open(filename, "rb") as f: f.seek(file_seek) for line in f: self.request.send(line) send_size += len(line) self.progress_bar(send_size, filesize) print() return send_size def accept_file(self, file_md5_path, old_filesize, filesize): """接收客户端发送来的文件内容""" f = open(file_md5_path, "ab") recv_size = old_filesize # 记录读取文件的大小 while recv_size < filesize: recv_data = self.request.recv(self.max_packet_size) self.file_md5.update(recv_data) f.write(recv_data) f.flush() # 主动将内存数据写入硬盘 recv_size += len(recv_data) self.progress_bar(recv_size, filesize) print() f.close() return recv_size def put(self, args): """实现文件上传""" print(args) old_file_md5 = args['file_md5'] filesize = args['filesize'] filename = args['filename'] user_dir_exist_size = HandleFile.get_file_size(self.user_dir) user_dir_exist_allsize = int(user_dir_exist_size) + int(filesize) if user_dir_exist_allsize > self.disk_quota: print("目前已经使用了%sMB,总共%sMB容量" % (user_dir_exist_size // 1024 // 1024, self.disk_quota // 1024 // 1024)) self.td.send_packget_dict({"code": "1011"}) return else: print("配额充足,允许上传") self.td.send_packget_dict({"code": "1012"}) file_md5_path = os.path.normpath( os.path.join(self.user_dir, old_file_md5)) file_name_path = os.path.normpath(os.path.join(self.user_dir, filename)) # 判断文件是否之前存在 self.file_md5 = hashlib.md5() # 需要计算文件的md5值,保证上传的完整性 if not os.path.exists(file_md5_path): old_filesize = 0 response = {'code': 1001} # 1001则代表是新文件 self.request.sendall(json.dumps(response).encode(self.coding)) recv_size = self.accept_file(file_md5_path, old_filesize, filesize) else: # 先计算一下已经上传的部分文件的md5值 with open(file_md5_path, "rb") as f1: for line in f1: self.file_md5.update(line) old_filesize = os.path.getsize(file_md5_path) # 1002则代表是已经存在文件,需要断点续传 response = {'code': 1002, 'old_filesize': old_filesize} self.request.sendall(json.dumps(response).encode(self.coding)) recv_size = self.accept_file(file_md5_path, old_filesize, filesize) shutil.move(file_md5_path, file_name_path) # 把md5名称的文件改名为最终的filename file_md5_code = self.file_md5.hexdigest() # 获取文件md5的值,字符串形式 self.request.send(b"ok") recv_md5_code = self.request.recv(self.max_packet_size).decode( self.coding) # 接收客户端发来的md5值 if recv_md5_code == file_md5_code: print("%s文件上传成功,大小为%s字节!" % (filename, recv_size)) self.request.send(b"1003") else: self.request.send(b"1004") def download(self, args): """实现文件下载""" filename = args['filename'] file_path = os.path.normpath(os.path.join(self.user_dir, filename)) if not os.path.exists(file_path): print('file:%s is not exists' % file_path) self.td.send_packget_dict({'code': "1009"}) return else: print("1010") self.td.send_packget_dict({'code': "1010"}) file_md5_code = self.file_md5_pro(file_path) filesize = os.path.getsize(file_path) # 拿到服务器文件的md5值 file_dict = {'filesize': filesize, 'file_md5_val': file_md5_code} # 发送服务端文件的大小 self.td.send_packget_dict(file_dict) # 接收code,看文件是否被下载过 file_dic = self.td.accept_packget_dict() exist_code = file_dic['code'] # 接下来传输文件 if exist_code == "1005": # 1005 表示客户端无该文件,全新下载 send_size = self.send_file(0, file_path, filesize) else: old_filesize = file_dic['old_filesize'] f_seek = old_filesize # 文件读取的偏移量 send_size = self.send_file(f_seek, file_path, filesize) # 发送文件的md5,判断文件一致性 file_md5_client = self.request.recv(self.max_packet_size).decode( self.coding) if file_md5_client == file_md5_code: print("%s文件下载成功,大小为%s字节!" % (filename, send_size)) self.request.send(b"1007") else: self.request.send(b"1008") def mk_dir(self, args): """新建文件夹""" filename = args['filename'] file_path = os.path.normpath(os.path.join(self.user_dir, filename)) if os.path.exists(file_path): self.td.send_packget_dict({"code": "1013"}) print("%s目录已经存在" % filename) return else: self.td.send_packget_dict({"code": "1014"}) try: os.mkdir(file_path) self.td.send_packget_dict({"code": "1015"}) print("创建目录成功") return except: self.td.send_packget_dict({"code": "1015"}) print("创建目录失败") return def checkfile(self, args): """查看文件夹及相关文件""" filename = args['filename'] if filename == ".": file_path = self.user_dir else: file_path = os.path.normpath(os.path.join(self.user_dir, filename)) print(file_path) if os.path.exists(file_path): hf = HandleFile() hf.get_file_tree(file_path) self.td.send_packget_dict({ "code": "1013", "dir": os.path.basename(file_path), "dir_tree": hf.tree }) else: self.td.send_packget_dict({"code": "1014"})
def __init__(self, socket, func_str): self.socket = socket self.func_str = func_str self.td = TransferDic(self.socket)
class MyTcpClient: """登陆之后的操作""" MENU = [('put', '上传'), ('download', '下载'), ('mk_dir', '新建文件夹'), ('checkfile', '查看目录下的文件夹及文件'), ('ch_dir', '进入下一级目录'), ('back', '返回上一级目录'), ('logout', '退出')] address_family = socket.AF_INET socket_type = socket.SOCK_STREAM allow_reuse_address = False max_packet_size = 8192 coding = 'utf-8' request_queue_size = 5 # client_download_dir = 'file_download' def __init__(self, socket, func_str): self.socket = socket self.func_str = func_str self.td = TransferDic(self.socket) def client_close(self): self.socket.close() def run(self): # while True: if hasattr(self, self.func_str): func = getattr(self, self.func_str) func(self.func_str) def file_md5(self, file_path): """ 计算文件的md5值并返回 :param file_path:文件路径 :return: """ md5 = hashlib.md5() with open(file_path, "rb") as f: for line in f: md5.update(line) return md5.hexdigest() def progress_bar(self, new_size, sum_size): """根据每次输入的大小判断目前的百分比""" bar = int(new_size / sum_size * 100) bar_num = bar // 3 print("\r%s|进度为:%s%%" % ("#" * bar_num, bar), end="") def send_file(self, file_seek, filename, filesize): send_size = file_seek # 最后发送需要传输的文件 with open(filename, 'rb') as f: f.seek(file_seek) for line in f: self.socket.sendall(line) # 后面发送的大小 send_size += len(line) self.progress_bar(send_size, filesize) print() return send_size def accept_file(self, file_md5_path, old_filesize, filesize, file_md5): """接收服务端发送来的文件内容""" f = open(file_md5_path, "ab") recv_size = old_filesize # 记录读取文件的大小 while recv_size < filesize: recv_data = self.socket.recv(self.max_packet_size) file_md5.update(recv_data) f.write(recv_data) f.flush() # 主动将内存数据写入硬盘 recv_size += len(recv_data) self.progress_bar(recv_size, filesize) print() f.close() return recv_size, file_md5 def put(self, action): """实现文件上传""" filename = input("输入需要上传的文件:") # 判断是否是文件 if not os.path.isfile(filename): print('file:%s is not exists' % filename) return filesize = os.path.getsize(filename) file_md5_val = self.file_md5(filename) head_dic = { 'action': action, 'filename': os.path.basename(filename), 'filesize': filesize, 'file_md5': file_md5_val } # 发送文件信息的数据字典给服务器 self.td.send_packget_dict(head_dic) disk_quota = self.td.accept_packget_dict() disk_quota_code = disk_quota['code'] if disk_quota_code == "1011": print("上传的文件超过配额") return # 接收服务端发送的数据,判断是否之前已经上传或者上传过一部分 recv_response = json.loads( self.socket.recv(self.max_packet_size).decode(self.coding)) file_md5_code = self.file_md5(filename) # 先读一遍文件获取到文件的md5的值 if recv_response['code'] == 1001: # 服务器无该文件,从头开始上传 send_size = self.send_file(0, filename, filesize) else: # 移动文件的光标到上次上传的最后接着上传 file_seek = int(recv_response['old_filesize']) send_size = self.send_file(file_seek, filename, filesize) # 发送文件的md5,判断文件一致性 self.socket.recv(self.max_packet_size) self.socket.sendall(file_md5_code.encode(self.coding)) # 发送文件的md5值给服务端 code = self.socket.recv(self.max_packet_size).decode(self.coding) if code == "1003": print("%s字节的%s文件上传成功!" % (send_size, filename)) else: print("上传失败,请重新上传!") def download(self, action): """实现文件下载""" filename = input("输入需要下载的文件:") download_dir = settings.home_path # file_path = os.path.normpath(os.path.join(download_dir, filename)) head_dic = {'action': action, 'filename': os.path.basename(filename)} self.td.send_packget_dict(head_dic) file_exist_code = self.td.accept_packget_dict()['code'] if file_exist_code == "1009": print('file:%s is not exists' % filename) return file_dict = self.td.accept_packget_dict() filesize = file_dict['filesize'] file_md5_val = file_dict['file_md5_val'] file_md5_path = os.path.normpath( os.path.join(download_dir, file_md5_val)) file_name_path = os.path.normpath(os.path.join(download_dir, filename)) file_md5 = hashlib.md5() # 计算文件的md5值,保证下载的完整性 if not os.path.exists(file_md5_path): # 新文件下载 file_dic = {'code': "1005"} # 1005则代表该文件从未下载过 self.td.send_packget_dict(file_dic) old_filesize = 0 recv_size, file_md5 = self.accept_file(file_md5_path, old_filesize, filesize, file_md5) else: old_filesize = os.path.getsize(file_md5_path) # 先计算一下已经下载的部分文件的md5值 with open(file_md5_path, "rb") as f1: for line in f1: file_md5.update(line) file_dic = { 'code': "1006", 'old_filesize': old_filesize } # 1006则代表该文件下载过 # 发送code给服务端,表示文件已经存在 self.td.send_packget_dict(file_dic) recv_size, file_md5 = self.accept_file(file_md5_path, old_filesize, filesize, file_md5) shutil.move(file_md5_path, file_name_path) # 把md5名称的文件改名为最终的filename file_md5_code = file_md5.hexdigest() # 获取文件md5的值,字符串形式 self.socket.sendall(file_md5_code.encode(self.coding)) # 发送文件的md5值给服务端 code = self.socket.recv(self.max_packet_size).decode(self.coding) if code == "1007": print("%s字节的%s文件下载成功!" % (recv_size, filename)) else: print("下载失败,请重新下载!") def mk_dir(self, action): """新建文件夹""" filename = input("目录名称:").strip() if not filename: return head_dic = {'action': action, 'filename': filename} self.td.send_packget_dict(head_dic) dir_exist_code = self.td.accept_packget_dict()['code'] if dir_exist_code == "1013": print("目录已经存在") return dir_create_code = self.td.accept_packget_dict()['code'] if dir_create_code == "1015": print("目录创建成功!") return else: print("目录创建失败!") return def checkfile(self, action): """查看文件夹及相关文件""" filename = input("输入查看的目录,.为当前目录:").strip() if not filename: return head_dic = {'action': action, 'filename': filename} self.td.send_packget_dict(head_dic) dir_msg = self.td.accept_packget_dict() dir_exist_code = dir_msg['code'] if dir_exist_code == "1014": print("目录不存在") return dir_name = dir_msg['dir'] dir_tree = dir_msg['dir_tree'] print("当前%s目录下文件结构如下:" % (dir_name)) print(dir_tree) return def logout(self, action): """退出""" quit("欢迎下次再来!")
def send_accept_server(self, dic): """发送消息给服务端并接受返回数据""" td = TransferDic(self.client) td.send_packget_dict(dic) recv_dic = td.accept_packget_dict() return recv_dic