网络编程与并发-FTP开发

FTP开发

要求:

 1. 用户加密认证
 2. 多用户同时登陆
 3. 每个用户有自己的家目录且只能访问自己的家目录
 4. 对用户进行磁盘配额、不同用户配额可不同
 5. 用户可以登陆server后,可切换目录
 6. 查看当前目录下文件
 7. 上传下载文件,保证文件一致性
 8. 传输过程中现实进度条
 9. 支持断点续传

服务器端

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = "Elijah"
__date__ = "2017/9/14 14:15"

import hmac
import json
import os
import pickle
import random
import socketserver
import string
import struct


class ftpserver(socketserver.BaseRequestHandler):
    max_packet_size = 8192
    coding = 'utf-8'
    # BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
    files_dir = '\\files\\'
    config_dir = '\\conf\\'
    download_dir = '\\download\\'

    def login(self):
        '''
        用户登陆
        :return: user_name
        '''
        user_exist = False
        flag = True
        while flag:
            print('用户进入登陆功能!即将进行用户名/密码验证......')
            with open(self.BASE_DIR + self.config_dir + 'config.txt', mode='rb') as f_r:
                config_dict = pickle.load(f_r)
            user_name = self.request.recv(1024)
            if (user_name == b'q') or (user_name == b'Q'):
                self.request.send(bytes('user_name quit', encoding=self.coding))
                print('用户在用户名阶段退出登陆功能!')
                break
            else:
                for k, v in config_dict.items():
                    if k == user_name:
                        self.request.send(bytes('user_name available', encoding=self.coding))
                        user_password = self.request.recv(1024)
                        if (user_password == b'q') or (user_password == b'Q'):
                            self.request.send(bytes('user_password quit', encoding=self.coding))
                            print('用户在密码阶段退出登陆功能!')
                            break
                        elif user_password == v[0]:
                            self.request.send(bytes('password available', encoding=self.coding))
                            client_respond = self.request.recv(1024)
                            if client_respond == b'get password available':
                                print('用户' + str(user_name, encoding=self.coding) + '密码正确!即将进行客户端验证......')
                                self.request.send(self.secret_key())
                                auth_result = self.request_auth()
                                if auth_result:
                                    self.request.send(bytes('auth_successful', encoding=self.coding))
                                    print('客户端验证成功!')
                                    flag = False
                                    user_exist = True
                                    break
                                else:
                                    self.request.send(bytes('auth_failed', encoding=self.coding))
                                    print('客户端验证失败!重新进行登录!')
                                    user_exist = True
                                    break
                            else:
                                print('客户端接收密码有效信息有误!')
                                break
                        else:
                            self.request.send(bytes('password wrong', encoding=self.coding))
                            print('用户输入密码有误!')
                            user_exist = True
                            break
                    else:
                        continue
                if not user_exist:
                    self.request.send(bytes('user_name wrong', encoding=self.coding))
                    print('用户名不存在,请重新输入!')

    def register(self):
        '''
        用户注册
        :return:
        '''
        print('用户进入注册功能!')
        flag = True
        dir_path = self.BASE_DIR + self.config_dir
        try:
            with open(os.path.join(dir_path, 'config.txt'), 'rb') as f_r:
                config_dict = pickle.load(f_r)
        except Exception as e:
            config_dict = {'': ['', '']}
        while flag:
            new_user = self.request.recv(1024)
            if (new_user == b'q') or (new_user == b'Q'):
                self.request.send(bytes('user quit', encoding=self.coding))
                print('用户退出注册功能!')
                break
            elif new_user in config_dict.keys():
                self.request.send(bytes('user exist', encoding=self.coding))
                print('用户名已经存在,无效用户名!用户重新输入!')
                continue
            elif new_user not in config_dict.keys():
                self.request.send(bytes('user available', encoding=self.coding))
                print('用户名有效!')
                new_password = self.request.recv(1024)
                if (new_password == b'q') or (new_password == b'Q'):
                    break
                else:
                    print('密码有效,接收上传目录空间配额')
                    self.request.send(bytes('password_received', encoding=self.coding))
                    user_space = self.request.recv(1024)
                    config_dict[new_user] = [new_password, user_space]
                    with open(self.BASE_DIR + self.config_dir + 'config.txt', mode='wb') as f_w:
                        pickle.dump(config_dict, f_w)
                    print('用户' + str(new_user, encoding=self.coding) + '注册成功! 密码为:' + str(new_password,
                                                                                          encoding=self.coding) + ' 用户上传目录空间为:' + str(
                        user_space, encoding=self.coding))
                    self.request.send(bytes('Register Successful', encoding=self.coding))
                    break
            else:
                print('服务端接受注册用户名有误!')
                break

    def secret_key(self):
        '''
        √生成随机校验码,以便进行认证
        :return: 6位bytes格式的数字+字母组合校验码
        '''
        auth_key = string.ascii_lowercase + string.digits
        character = random.sample(auth_key, 6)
        string_key = "".join(character)
        bytes_key = bytes(string_key.encode(self.coding))
        return bytes_key

    def request_auth(self):
        '''
        认证客户端链接
        :return: 布尔值
        '''
        print('开始进行客户端验证......')
        msg = os.urandom(32)
        self.request.send(msg)
        key = self.request.recv(1024)
        self.request.send(bytes('received', encoding=self.coding))
        h = hmac.new(key, msg)
        digest = h.digest()
        respone = self.request.recv(len(digest))
        return hmac.compare_digest(respone, digest)

    def get_directorysize(self, file_path, size=0):
        '''
        查看filePath目录下的文件以及大小,用于判断用户配额
        :param filePath:
        :param size:
        :return:
        '''
        for root, dirs, files in os.walk(file_path):
            # print('用户的目录下包含:')
            for f in files:
                size += os.path.getsize(os.path.join(root, f))
                # print('文件:\033[4m %s \033[0m   大小:\033[1;32m %.2f \033[0m MB' % (f, ((os.path.getsize(os.path.join(root, f))) / 1048576)))
        return size

    def upload(self, head_dic):
        '''
        文件上传功能
        :param args:报头字典{'command': 操作命令, 'file_name': 文件名称, 'file_size': 文件大小,'user_name': 用户名}
        :return:
        '''
        # 读取用户磁盘限额
        with open(self.BASE_DIR + self.config_dir + 'config.txt', 'rb') as f:
            config_dict = pickle.load(f)
            space = config_dict[bytes(head_dic['user_name'],encoding=self.coding)][1]
            user_space = int(str(space,encoding=self.coding))
        while True:
            # 文件路径-每个用户一个目录
            # f_name = head_dic['file_name'].split('/')[-1]
            f_name = os.path.basename(head_dic['file_name'])
            file_path = self.BASE_DIR + self.files_dir + head_dic['user_name'] + '\\'
            try:
                os.mkdir(file_path)
            except Exception as e:
                pass
            # 文件大小
            file_size = head_dic['file_size']
            # 判断文件是否已经存在,若存在是否断点续传
            if os.path.exists(os.path.join(file_path, f_name)):
                print('用户上传文件已经存在,询问用户是否继续上传...')
                self.request.send(bytes('file_already_exist', encoding=self.coding))
                is_continue = self.request.recv(1024)
                if is_continue == b'continue_upload':
                    get_existfile_size = os.path.getsize(os.path.join(file_path, f_name))
                    temp_size = get_existfile_size
                    self.request.send(bytes(str(get_existfile_size), encoding=self.coding))
                    with open(os.path.join(file_path, f_name), 'ab') as f:
                        while temp_size < file_size:
                            recv_continue_data = self.request.recv(self.max_packet_size)
                            f.write(recv_continue_data)
                            f.flush()
                            temp_size += len(recv_continue_data)
                            print('文件继续传进度:' + str(round((temp_size / 1024), 2)) + ' KB / ' + str(
                                round((file_size / 1024), 2)) + ' KB')
                    print('客户端:' + head_dic['user_name'] + ' 文件:' + f_name + ' 上传完成!')
                    break
            else:
                # 判断用户配额空间是否充足-每个用户配额 10485760 字节
                directory_size = self.get_directorysize(file_path)
                if (directory_size + file_size) > user_space * 1024 * 1024:
                    print('用户:' + head_dic['user_name'] + ' 上传文件后目录空间将超过 ' + str(user_space) + ' MB 限额,上传文件失败!')
                    self.request.send(bytes('Insufficient_directory_space', encoding=self.coding))
                    client_received = self.request.recv(1024)
                    if client_received == b'Insufficient_directory_space_received':
                        self.request.send(space)
                        break
                else:
                    self.request.send(bytes('Directory_space_available', encoding=self.coding))
                    # 接收到的文件大小
                    recv_size = 0
                    print('----->', file_path)
                    with open(os.path.join(file_path, f_name), 'wb') as f_w:  # 必须这个打开文件,不然如果没有该文件的话报错
                        # with open(file_path, 'wb') as f_w:
                        while recv_size < file_size:
                            recv_data = self.request.recv(self.max_packet_size)
                            f_w.write(recv_data)
                            f_w.flush()
                            recv_size += len(recv_data)
                            print('文件上传进度:' + str(round((recv_size / 1024), 2)) + ' KB / ' + str(
                                round((file_size / 1024), 2)) + ' KB')
                    print('客户端:' + head_dic['user_name'] + ' 文件:' + f_name + ' 上传完成!')
                    break

    def download(self, head_dic):
        '''
        下载功能
        :param head_dic:
        :param user_name:
        :return:
        '''
        file_path = self.BASE_DIR + self.files_dir + head_dic['user_name'] + '\\'
        while True:
            send_size = 0
            files_dict = {}
            for root, dirs, files in os.walk(file_path):
                for f in files:
                    files_dict[f] = os.path.getsize(os.path.join(root, f))
            # 服务端将目录字典序列化
            files_dict_json = json.dumps(files_dict)
            # 服务端将序列化的目录字典bytes化
            files_dict_json_bytes = bytes(files_dict_json, encoding=self.coding)
            # 将bytes化的目录字典打包
            files_dict_json_bytes_struct = struct.pack('i', len(files_dict_json_bytes))
            # 服务端将打包的字典发送给客户端
            self.request.send(files_dict_json_bytes_struct)
            # 服务端接收客户端是否收到报头
            is_received = self.request.recv(1024)
            if is_received == b'files_dict_json_bytes_struct_received':
                self.request.send(files_dict_json_bytes)
                print('用户:' + head_dic['user_name'] + ' 收到上传目录文件字典!')
            else:
                print('客户端返回信息有误,请重试!')
                break
            f_name = self.request.recv(1024)
            self.request.send(bytes('file_name_received', encoding=self.coding))
            file_path = self.BASE_DIR + self.files_dir + head_dic['user_name'] + '\\'
            # with open(os.path.join(file_path, os.path.basename(f_name)), 'rb') as f:
            with open(file_path + str(f_name, encoding=self.coding), 'rb') as f:
                for line in f:
                    self.request.send(line)
                    send_size += len(line)
                    print(('文件下载进度:%.2f KB / %.2f KB') % (
                        send_size / 1024, (files_dict[str(f_name, encoding=self.coding)]) / 1024))
                is_finished = self.request.recv(1024)
                if is_finished == b'received_finished':
                    print('文件 ' + str(f_name, encoding=self.coding) + ' 下载完成!\n')
                    break
                else:
                    print('客户端接收错误,请重试!')
                    break

    def show_dir(self, head_dic):
        '''
        查看上传目录下文件
        :param head_dic:
        :param user_name:
        :return:
        '''
        file_path = self.BASE_DIR + self.files_dir + head_dic['user_name'] + '\\'
        while True:
            files_dict = {}
            for root, dirs, files in os.walk(file_path):
                for f in files:
                    files_dict[f] = os.path.getsize(os.path.join(root, f))
            # 服务端将目录字典序列化
            files_dict_json = json.dumps(files_dict)
            # 服务端将序列化的目录字典bytes化
            files_dict_json_bytes = bytes(files_dict_json, encoding=self.coding)
            # 将bytes化的目录字典打包
            files_dict_json_bytes_struct = struct.pack('i', len(files_dict_json_bytes))
            # 服务端将打包的字典发送给客户端
            self.request.send(files_dict_json_bytes_struct)
            # 服务端接收客户端是否收到报头
            is_received = self.request.recv(1024)
            if is_received == b'files_dict_json_bytes_struct_received':
                self.request.send(files_dict_json_bytes)
                print('用户:' + head_dic['user_name'] + ' 收到上传目录文件字典!')
                break
            else:
                print('客户端返回信息有误,请重试!')
                break

    def delete_file(self, head_dic):
        '''
        删除用户目录文件
        :param head_dic:
        :return:
        '''
        file_path = self.BASE_DIR + self.files_dir + head_dic['user_name'] + '\\'
        while True:
            send_size = 0
            files_dict = {}
            for root, dirs, files in os.walk(file_path):
                for f in files:
                    files_dict[f] = os.path.getsize(os.path.join(root, f))
            # 服务端将目录字典序列化
            files_dict_json = json.dumps(files_dict)
            # 服务端将序列化的目录字典bytes化
            files_dict_json_bytes = bytes(files_dict_json, encoding=self.coding)
            # 将bytes化的目录字典打包
            files_dict_json_bytes_struct = struct.pack('i', len(files_dict_json_bytes))
            # 服务端将打包的字典发送给客户端
            self.request.send(files_dict_json_bytes_struct)
            # 服务端接收客户端是否收到报头
            is_received = self.request.recv(1024)
            if is_received == b'files_dict_json_bytes_struct_received':
                self.request.send(files_dict_json_bytes)
                print('用户:' + head_dic['user_name'] + ' 收到上传目录文件字典!')
            else:
                print('客户端返回信息有误,请重试!')
                break
            f_name = self.request.recv(1024)
            self.request.send(bytes('file_name_received', encoding=self.coding))
            file_path = self.BASE_DIR + self.files_dir + head_dic['user_name'] + '\\'
            # with open(file_path+str(f_name,encoding=self.coding), 'rb') as f:
            #     for line in f:
            #         self.request.send(line)
            #         send_size += len(line)
            #         print(('文件下载进度:%.2f KB / %.2f KB')%(send_size,(files_dict[str(f_name,encoding=self.coding)])))
            is_ready_for_delete = self.request.recv(1024)
            if is_ready_for_delete == b'ready_for_delete':
                os.remove(file_path + str(f_name, encoding=self.coding))
                self.request.send(bytes('deleted_finished', encoding=self.coding))
                print(('文件:%s 删除完成!') % str(f_name, encoding=self.coding))
                break
            else:
                print('客户端接收错误,请重试!')
                break

    def handle(self):
        '''
        handle方法
        :return:
        '''
        # 循环体—用户登陆、注册
        while True:
            user_choice = self.request.recv(1024)
            if user_choice == b'1':  # 登陆
                self.login()
                print('--->这话可以删除了--->进入下一阶段功能选择!')
                break
            elif user_choice == b'2':  # 注册
                self.register()
            else:
                self.request.send(bytes('对不起,您的输入有误!请重新输入!', encoding=self.coding))
                print('用户输入有误!')
                continue
        # 循环体—开始进行通讯
        while True:
            try:
                # 接受固定报头4字节
                head_struct = self.request.recv(4)
                self.request.send(bytes('head_struct_received', encoding=self.coding))
                if not head_struct:
                    break
                # 解包报头,取出第一个报头长度
                head_len = struct.unpack('i', head_struct)[0]
                # 根据报头长度,收取序列化的字典对象
                head_json = self.request.recv(head_len).decode(self.coding)
                # 反序列化,收取字典
                head_dic = json.loads(head_json)
                # 报头字典{'command': 操作命令, 'file_name': 文件名称, 'file_size': 文件大小,'user_name': 用户名}
                # self.request.send(head_dic)
                cmd = head_dic['command']
                if hasattr(self, cmd):
                    func = getattr(self, cmd)
                    func(head_dic)
            except Exception:
                break


if __name__ == '__main__':
    server_obj = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), ftpserver)
    server_obj.serve_forever()

客户端

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = "Elijah"
__date__ = "2017/9/14 15:48"

import hmac
import json
import os
import socket
import struct
import pickle
from tkinter.filedialog import askopenfilename, askdirectory


class ftpclient:
    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

    def __init__(self, server_address, connect=True):
        '''
        √初始化客户端
        :param server_address: 服务器IP与Port信息
        :param connect: 是否立即创建连接,默认选项True
        '''
        self.server_address = server_address
        self.socket = socket.socket(self.address_family, self.socket_type)
        if connect:
            try:
                self.client_connect()
            except:
                self.client_close()
                raise

    def client_connect(self):
        '''
        √客户端建立连接
        :return:
        '''
        self.socket.connect(self.server_address)

    def client_close(self):
        '''
        √客户端关闭连接
        :return:
        '''
        self.socket.close()

    def conn_auth(self, key):
        '''
        验证客户端到服务器的链接
        :param conn:
        :return:
        '''
        msg = self.socket.recv(32)
        h = hmac.new(key, msg)
        self.socket.send(key)
        info = self.socket.recv(1024)
        if info == b'received':
            digest = h.digest()
            self.socket.sendall(digest)

    def upload(self, head_dic):
        '''
        上传文件
        :param head_dic:
        :return:
        '''
        while True:
            head_json = json.dumps(head_dic)
            head_json_bytes = bytes(head_json, encoding=self.coding)
            head_struct = struct.pack('i', len(head_json_bytes))
            self.socket.send(head_struct)
            is_received = self.socket.recv(1024)
            if is_received == b'head_struct_received':
                self.socket.send(head_json_bytes)
                send_size = 0
                is_dirspaceok_or_continue = self.socket.recv(1024)
                if is_dirspaceok_or_continue == b'file_already_exist':
                    is_continue = input('对不起,您选择的文件\033[4m ' + os.path.basename(
                        head_dic['file_name']) + ' \033[0m已经在服务端存在,是否选择断点续传(y/n)?\n>>>').strip()
                    if is_continue.lower() == 'y':
                        self.socket.send(bytes('continue_upload', encoding=self.coding))
                        existfile_size = self.socket.recv(1024)
                        with open(head_dic['file_name'], 'rb') as f:
                            f.seek(int(existfile_size))
                            for line in f:
                                self.socket.send(line)
                                send_size += len(line)
                                print('文件上传进度:' + str(round((send_size / 1024), 2)) + ' KB / ' + str(
                                    round((head_dic['file_size'] / 1024), 2)) + ' KB')
                            print('文件 ' + os.path.basename(head_dic['file_name']) + ' 上传完成!\n')
                            break
                    else:
                        print('您不选择断点续传功能,请重新选择上传文件!')
                        break
                elif is_dirspaceok_or_continue == b'Insufficient_directory_space':
                    self.socket.send(bytes('Insufficient_directory_space_received',encoding=self.coding))
                    u_space = self.socket.recv(1024)
                    print('对不起,您选择的文件\033[4m ' + os.path.basename(head_dic['file_name']) + ' \033[0m上传后将导致目录超过 \033[0;32m'+str(u_space,encoding=self.coding)+' MB \033[0m 配额空间,请选择删除文件功能移除目录下的文件以便可以再次上传,谢谢!\n')
                    break
                elif is_dirspaceok_or_continue == b'Directory_space_available':
                    with open(head_dic['file_name'], 'rb') as f:
                        for line in f:
                            self.socket.send(line)
                            send_size += len(line)
                            print('文件上传进度:' + str(round((send_size / 1024), 2)) + ' KB / ' + str(
                                round((head_dic['file_size'] / 1024), 2)) + ' KB')
                        # print('文件 ' + head_dic['file_name'].split('/')[-1] + ' 上传完成!\n')
                        print('文件 ' + os.path.basename(head_dic['file_name']) + ' 上传完成!\n')
                        break
                else:
                    print('对不起,服务端接收错误,请重试!')
                    break
            else:
                print('对不起,服务器端没有收到报头结构信息!')
                continue

    def download(self, head_dic):
        '''
        下载
        :return:
        '''
        while True:
            count = 0
            files_list = []
            head_json = json.dumps(head_dic)
            head_json_bytes = bytes(head_json, encoding=self.coding)
            head_struct = struct.pack('i', len(head_json_bytes))
            self.socket.send(head_struct)
            is_received = self.socket.recv(1024)
            if is_received == b'head_struct_received':
                self.socket.send(head_json_bytes)
                recv_size = 0
                # 客户端收目录报头打包
                files_dict_json_bytes_struct = self.socket.recv(4)
                # 客户端发送收到报头信息
                self.socket.send(bytes('files_dict_json_bytes_struct_received', encoding=self.coding))
                if not files_dict_json_bytes_struct:
                    break
                # 客户端解包,得到序列化字典长度
                files_dict_json_len = struct.unpack('i', files_dict_json_bytes_struct)[0]
                # 客户端得到序列化字典
                files_dict_json = self.socket.recv(files_dict_json_len).decode(self.coding)
                # 客户端得到目录文件字典{'文件名':'文件字节大小'}
                files_dict = json.loads(files_dict_json)
                print('用户:' + head_dic['user_name'] + '的目录为:')
                for k, v in files_dict.items():
                    count += 1
                    files_list.append(k)
                    print(('%s 、文件:\033[4m %s \033[0m   大小:\033[1;32m %.2f \033[0m MB') % (count, k, (v / 1048576)))
                user_download_choice = input('请输入要下载文件的编号:\n>>>').strip()
                for i, val in enumerate(files_list):
                    if int(user_download_choice) == (i + 1):
                        download_filename = val
                        self.socket.send(bytes(download_filename, encoding=self.coding))
                        break
                    else:
                        download_filename = ''
                is_filename_received = self.socket.recv(1024)
                if (download_filename != '') and (is_filename_received == b'file_name_received'):
                    with open(os.path.join(head_dic['file_name'], os.path.basename(download_filename)),
                              'wb') as f_w:  # 必须这个打开文件,不然如果没有该文件的话报错
                        # with open(file_path, 'wb') as f_w:
                        while recv_size < int(files_dict[download_filename]):
                            recv_data = self.socket.recv(self.max_packet_size)
                            f_w.write(recv_data)
                            recv_size += len(recv_data)
                            print(('文件下载进度:%.2f KB / %.2f KB') % (
                                recv_size / 1024, (files_dict[download_filename]) / 1024))
                    self.socket.send(bytes('received_finished', encoding=self.coding))
                    print('用户:' + head_dic['user_name'] + ' 文件:' + download_filename + ' 下载完成!\n')
                    break
                else:
                    print('选择的文件不存在!')
                    continue
            else:
                print('对不起,服务器端没有收到报头结构信息!')
                continue

    def show_dir(self, head_dic):
        '''
        查看用户上传目录
        :return:
        '''
        while True:
            head_json = json.dumps(head_dic)
            head_json_bytes = bytes(head_json, encoding=self.coding)
            head_struct = struct.pack('i', len(head_json_bytes))
            self.socket.send(head_struct)
            is_received = self.socket.recv(1024)
            if is_received == b'head_struct_received':
                self.socket.send(head_json_bytes)
                # 客户端收目录报头打包
                files_dict_json_bytes_struct = self.socket.recv(4)
                # 客户端发送收到报头信息
                self.socket.send(bytes('files_dict_json_bytes_struct_received', encoding=self.coding))
                if not files_dict_json_bytes_struct:
                    break
                # 客户端解包,得到序列化字典长度
                files_dict_json_len = struct.unpack('i', files_dict_json_bytes_struct)[0]
                # 客户端得到序列化字典
                files_dict_json = self.socket.recv(files_dict_json_len).decode(self.coding)
                # 客户端得到目录文件字典{'文件名':'文件字节大小'}
                files_dict = json.loads(files_dict_json)
                print('用户:' + head_dic['user_name'] + '的上传目录为:')
                for k, v in files_dict.items():
                    print(('文件:\033[4m %s \033[0m   大小:\033[1;32m %.2f \033[0m MB') % (k, (v / 1048576)))
                break
            else:
                print('对不起,服务器端没有收到报头结构信息!')
                continue

    def delete_file(self, head_dic):
        '''
        删除用户目录下文件
        :param head_dic:
        :return:
        '''
        while True:
            count = 0
            files_list = []
            head_json = json.dumps(head_dic)
            head_json_bytes = bytes(head_json, encoding=self.coding)
            head_struct = struct.pack('i', len(head_json_bytes))
            self.socket.send(head_struct)
            is_received = self.socket.recv(1024)
            if is_received == b'head_struct_received':
                self.socket.send(head_json_bytes)
                recv_size = 0
                # 客户端收目录报头打包
                files_dict_json_bytes_struct = self.socket.recv(4)
                # 客户端发送收到报头信息
                self.socket.send(bytes('files_dict_json_bytes_struct_received', encoding=self.coding))
                if not files_dict_json_bytes_struct:
                    break
                # 客户端解包,得到序列化字典长度
                files_dict_json_len = struct.unpack('i', files_dict_json_bytes_struct)[0]
                # 客户端得到序列化字典
                files_dict_json = self.socket.recv(files_dict_json_len).decode(self.coding)
                # 客户端得到目录文件字典{'文件名':'文件字节大小'}
                files_dict = json.loads(files_dict_json)
                print('用户:' + head_dic['user_name'] + '的目录为:')
                for k, v in files_dict.items():
                    count += 1
                    files_list.append(k)
                    print(('%s 、文件:\033[4m %s \033[0m   大小:\033[1;32m %.2f \033[0m MB') % (count, k, (v / 1048576)))
                user_delete_choice = input('请输入要\033[1;31m删除\033[0m文件的编号:\n>>>').strip()
                for i, val in enumerate(files_list):
                    if int(user_delete_choice) == (i + 1):
                        delete_filename = val
                        self.socket.send(bytes(delete_filename, encoding=self.coding))
                        break
                    else:
                        delete_filename = ''
                is_filename_received = self.socket.recv(1024)
                if (delete_filename != '') and (is_filename_received == b'file_name_received'):
                    # with open(os.path.join(head_dic['file_name'], os.path.basename(delete_filename)), 'wb') as f_w: # 必须这个打开文件,不然如果没有该文件的话报错
                    #     # with open(file_path, 'wb') as f_w:
                    #     while recv_size < int(files_dict[delete_filename]):
                    #         recv_data = self.socket.recv(self.max_packet_size)
                    #         f_w.write(recv_data)
                    #         recv_size += len(recv_data)
                    #         print(('文件下载进度:%.2f KB / %.2f KB')%(recv_size,(files_dict[delete_filename])))
                    self.socket.send(bytes('ready_for_delete', encoding=self.coding))
                    is_finished = self.socket.recv(1024)
                    if is_finished == b'deleted_finished':
                        print('用户:' + head_dic['user_name'] + ' 文件:\033[4m ' + delete_filename + ' \033[0m删除完成!\n')
                        break
                    else:
                        print('对不起,删除文件出错,请重试!')
                        break
                else:
                    print('选择的文件不存在或删除失败,请重试!')
                    continue
            else:
                print('对不起,服务器端没有收到报头结构信息!')
                continue

    def user_exit(self, head_dic):
        print('用户:' + head_dic['user_name'] + '退出!')
        exit()

    def operation(self):
        '''
        用户执行功能
        :return:
        '''
        # 用户登陆
        u_space = 0
        end_loop = True
        while end_loop:
            input_choice = input('您好,请输入您要选择功能的对应编号:\n1、登陆\n2、注册\n3、退出\n>>>').strip()
            if input_choice == '1':  # 登陆
                self.socket.send(input_choice.encode(self.coding))
                while True:
                    user_name = input('你好,请输入用户名(或者输入q退出):\n>>>').strip()
                    self.socket.send(user_name.encode(self.coding))
                    data_username = self.socket.recv(1024)
                    if data_username == b'user_name quit':
                        print('用户退出登陆程序!')
                        break
                    elif data_username == b'user_name available':
                        user_password = input('欢迎你!' + user_name + '请输入密码(或者输入q退出):\n>>>').strip()
                        self.socket.send(user_password.encode(self.coding))
                        data_password = self.socket.recv(1024)
                        if data_password == b'user_password quit':
                            print('用户退出登陆程序!')
                            break
                        elif data_password == b'password available':
                            self.socket.send(bytes('get password available', encoding=self.coding))
                            print('密码正确!进行客户端验证!')
                            data_key = self.socket.recv(1024)
                            print('接收到从服务端发来的验证码 ' + str(data_key, encoding=self.coding) + ' 正在进行验证....')
                            self.conn_auth(data_key)
                            data_authresult = self.socket.recv(1024)
                            if data_authresult == b'auth_successful':
                                print('客户端验证成功!进入下一阶段功能选择!')
                                end_loop = False
                                break
                            elif data_authresult == b'auth_failed':
                                print('验证失败,请重新进行登录操作!谢谢!')
                                continue
                        elif data_password == b'password wrong':
                            print('对不起,您输入的密码有误,请重新输入!')
                            continue
                        else:
                            print('对不起,您的输入有误,请重新输入!')
                            continue
                    elif data_username == b'user_name wrong':
                        print('对不起,您输入的用户名有误!')
                        continue
            elif input_choice == '2':  # 注册
                self.socket.send(input_choice.encode(self.coding))
                while True:
                    new_user = input('你好,请输入要注册的用户名(或者输入q退出):\n>>>').strip()
                    self.socket.send(new_user.encode(self.coding))
                    data = self.socket.recv(1024)
                    if data == b'user exist':
                        print('对不起,您输入的用户名已经被注册,请重新输入!')
                        continue
                    elif data == b'user available':
                        new_password = input('用户名有效,请输入密码!(或者输入q退出)\n>>>').strip()
                        self.socket.send(new_password.encode(self.coding))
                        is_received = self.socket.recv(1024)
                        if is_received == b'password_received':
                            user_space = input('请输入用户上传目录的空间配额!(或者输入q退出):\n(默认10MB)>>>').strip()
                            self.socket.send(bytes(user_space, encoding=self.coding))
                            data = self.socket.recv(1024)
                            if data == b'Register Successful':
                                print('用户' + new_user + '注册成功! 密码为:' + new_password + ' 用户上传目录空间为:' + user_space + 'MB')
                                u_space = user_space
                                break
                            else:
                                print('对不起,注册失败,请重新注册!')
                                continue
                        else:
                            print('对不起,服务端接收密码有误,请重新注册!')
                            continue
                    elif data == b'user quit':
                        print('用户退出注册功能!')
                        break
            elif input_choice == '3':
                print('欢迎下次使用!')
                exit()
            else:
                print('对不起,您输入的功能编号有误,请重新输入!')
                continue

        # 进行功能选择
        end_func = True
        choice_dict = {'1': 'upload', '2': 'download', '3': 'show_dir', '4': 'delete_file', '5': 'user_exit', }
        while end_func:
            func_choice = input('用户:' + user_name + ' 请选择功能:\n1、上传文件\n2、下载文件\n3、查看目录\n4、删除文件\n5、退出\n>>>:').strip()
            user_command = choice_dict[func_choice]
            if (func_choice == '1'):
                file_name = askopenfilename(filetypes=(("All files", "*.*"),))
                file_size = os.path.getsize(file_name)
                head_dic = {'command': user_command, 'file_name': file_name, 'file_size': file_size,
                            'user_name': user_name}
            elif func_choice == '2':
                file_name = askdirectory(title='选择下载至目录')
                file_size = ''
                head_dic = {'command': user_command, 'file_name': file_name, 'file_size': file_size,
                            'user_name': user_name}
            else:
                file_name = ''
                file_size = ''
                head_dic = {'command': user_command, 'file_name': file_name, 'file_size': file_size,
                            'user_name': user_name}
            if hasattr(self, user_command):
                func = getattr(self, user_command)
                func(head_dic)


if __name__ == '__main__':
    client = ftpclient(('127.0.0.1', 8080))
    client.operation()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值