• python day 18: thinking in UML与FTP作业重写


    python day 18

    2019/10/29

    1. thinking in UML读书小感

    这3天在看谭云杰的thinking in UML这本书,500多页的PDF扫描版,现在只看到279页,算是看完了一半,很多概念都是半懂不懂的,如在云山雾罩中一样。虽然看得不太明白,但是也有一些小感悟。

    1. 代码并不是全部,前期的需求分析,建模设计才是重点,这个就像行军打仗做好作战方略,备好粮草一样,后面的代码就是排兵步阵了。
    2. UML能看懂,不代表会画,会画的人必定是懂得RUP的人,这也解释了我这个初学者连一个小小的多用户登录FTP的程序的用例图都没画好的原因。
    3. 目标问题,我的目标是学会python,先掌握一门语言,而不是先上来就更高级的系统分析,有点好高骛远了。
    4. 不过,这本书看到,然后现在停下来,还是对我有不小的收获,对于整个软件开发的全貌有了不同的认识。同时也理解为什么很多公司不愿意招培训班或者自学的人了,因为如果只会写代码,像一些沟通用图如UML没有掌握,团队之间就不好沟通。

    2. FTP作业重写

    2.1 软件目录结构

    脚本目录结构

    按照老师的讲解,在命令行模式下输入python FTPServer.py start.
    另一个命令行输入python FTPClient.py -s 127.0.0.1 -p 9999

    2.2 FTPClient端脚本

    2.2.1 bin目录下的FTPClient.py模块

    import os
    import sys
    
    sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    from lib import client
    
    if __name__ == '__main__':
        
        client.Client(sys.argv)
    

    2.2.2 config目录下的settings.py模块

    import os
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    USER_HOME = os.path.join(BASE_DIR, "db", "users")
    

    2.2.3 lib目录下的client.py模块

    import os
    import sys
    
    sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    import hashlib
    import json
    import socket
    from config import settings
    import getpass
    import time
    
    
    class Client(object):
        def __init__(self, sys_argv):
            self.USER_HOME = settings.USER_HOME
            self.cwd = ""
            self.args = sys_argv
            self.HOST_IP = None
            self.HOST_PORT = None
            self.sock = None
            self.logout_flag = False
            self.response_code_dict = {
                "100": "user successfully registerd",
                "101": "username already existed,enter another username",
                "200": "pass users authentication",
                "201": "wrong username or password",
                "202": "user does not exist",
                "300": "ready to get file from server",
                "301": "ready to send to server",
                "302": "file doesn't exist on ftp server",
                "303": "storage is full",
                "601": "changed directory",
                "602": "failed to find directory",
                "2003": "already existed",
                "2004": 'continue put',
                "2005": "directory created"
            }
            self.argv_parse()
    
        def argv_parse(self):
            if len(self.args) < 5:
                self.help_msg()
                sys.exit()
            else:
                mandatory_fields = ['-s', '-p']
                for i in mandatory_fields:
                    if i not in self.args:
                        self.help_msg()
                        sys.exit()
                try:
                    self.HOST_IP = self.args[self.args.index('-s') + 1]
                    self.HOST_PORT = int(self.args[self.args.index('-p') + 1])
                    self.handle()
                except (IndexError, ValueError):
                    # 如果有索引错误,就打印帮助信息并退出程序
                    self.help_msg()
                    sys.exit("hhhh")
    
        def help_msg(self):
            msg = """
            input like below:
    
            python FTPClient.py -s 127.0.0.1 -p 9999
            """
            print(msg)
    
        def handle(self):
            self.connect(self.HOST_IP, self.HOST_PORT)
            while True:
                username = input("username:>>>").strip()
                password = getpass.getpass("password:>>>").strip()
                md5 = hashlib.md5("lan".encode("utf-8"))
                md5.update(password.encode("utf-8"))
                password = md5.hexdigest()
                user_pwd_dict = {"username": username, "password": password}
                user_pwd = json.dumps(user_pwd_dict)
                inp = input("请输入数字:1是登录,2是注册(q退出):>>>").strip()
                if len(inp) < 1:
                    continue
                if inp == '1':
                    if self.auth(user_pwd):
                        self.interactive()
                elif inp == '2':
                    self.register(user_pwd)
                elif inp.lower() == 'q':
                    break
                else:
                    print("Invalid input")
            self.sock.close()
    
        def connect(self, ip, port):
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.connect((ip, port,))
    
        def auth(self, user_pwd):
            '''
            验证用户名与密码
            :param user_pwd:
            :return:
            '''
            self.sock.sendall(("user_auth|%s" % user_pwd).encode("utf-8"))
            data = json.loads(self.sock.recv(1024).decode("utf-8"))
            if data["result"] == "200":
                self.username = data["username"]
                self.cwd = data["user_home"]
                self.storage_limit = data["storage_limit"]
                self.storage_used = data["storage_used"]
                return True
            elif data["result"] == "202":
                print(self.response_code_dict["202"])
                return False
            else:
                print(self.response_code_dict["201"])
                return False
    
        def register(self, user_pwd):
            '''
            注册用户
            :param user_pwd:
            :return:
            '''
            self.sock.sendall(("user_register|%s" % user_pwd).encode("utf-8"))
            msg = self.sock.recv(1024)
            if msg == b"100":
                print(self.response_code_dict["100"])
            elif msg == b"101":
                print(self.response_code_dict["101"])
    
        def interactive(self):
            while not self.logout_flag:
                cmd = input("[%s %s]:" % (self.username, self.cwd)).strip()
                if len(cmd) < 1: continue
                cmd_str = "cmd_" + cmd.split()[0]
    
                if hasattr(self, cmd_str):
                    func = getattr(self, cmd_str)
                    func(cmd)
                else:
                    print("Invalid command")
    
        def cmd_cd(self, cmd):
            '''
            切换路径
            :param cmd:
            :return:
            '''
            if len(cmd.split()) < 2: pass
            input_path = cmd.split()[1].strip()
            print("try_path:>>>", input_path)
            dir_list = self.cwd.split(os.sep)
            print("dir_list:>>>", dir_list)  # ["lanxing",""]
            if dir_list[-1] == "":
                del dir_list[-1]
            new_path = ""
            if input_path == "..":
                del dir_list[-1]  # []
                if len(dir_list) > 0:
                    new_path = os.sep.join(dir_list) + os.sep
            else:
                if input_path.startswith(self.cwd):
                    new_path = input_path
                else:
                    new_path = os.path.join(self.cwd, input_path) + os.sep
            print("new_path:>>>", new_path)
            if not new_path.startswith(self.username):
                pass
            else:
                data = {"cwd": new_path}
                data_json = json.dumps(data)
                self.sock.sendall(("cd|%s" % data_json).encode("utf-8"))
                server_reply = self.sock.recv(1024)
                auth_result = json.loads(server_reply.decode("utf-8"))
                if auth_result["result"] == "601":
                    self.cwd = new_path
                else:
                    print(self.response_code_dict["602"])
    
        def cmd_ls(self, cmd):
            msg = ""
            if len(cmd.split()) == 1:
                msg = json.dumps({"cwd": self.cwd})
    
            elif len(cmd.split()) == 2:
                path = cmd.split()[1]
                dir_list = self.cwd.split()
                new_path = ""
                if dir_list[-1] == "":
                    del dir_list[-1]
                if path == "..":
                    del dir_list[-1]
                    if len(dir_list) > 0:
                        new_path = os.sep.join(dir_list) + os.sep
                else:
                    if path.startswith(self.user_def_cwd):
                        new_path = path
                    else:
                        new_path = self.user_def_cwd + path
    
                if not new_path.startswith(self.user_def_cwd):
                    pass
                msg = json.dumps({"cwd": new_path})
            self.sock.sendall(("ls|{0}".format(msg)).encode("utf-8"))
            msg_size = int(self.sock.recv(1024).decode("utf-8"))
            self.sock.sendall(b"300")
            has_received = 0
            auth_result = ""
            while has_received < msg_size:
                data = self.sock.recv(1024)
                auth_result += data.decode("utf-8")
                has_received += len(data)
            auth_result = json.loads(auth_result)
            if auth_result["result"]:
                print(auth_result["result"])
            else:
                print(self.response_code_dict["302"])
    
        def cmd_put(self, cmd):
            data = {"file_size": None, "file_name": None, "dst_path": None}
    
            if len(cmd.split()) == 2:
                src_file = cmd.split()[1]
                dst_path = self.cwd
            else:
                src_file, dst_path = cmd.split()[1], cmd.split()[2]
            if len(src_file.split(os.sep)) == 1:
                src_file = os.path.join(settings.BASE_DIR, "bin", src_file)
            if os.path.isfile(src_file):
                file_size = os.stat(src_file).st_size
                data["file_size"] = file_size
                data["file_name"] = os.path.basename(src_file)
                if dst_path.startswith("lanxing"):
                    data["dst_path"] = dst_path
                else:
                    print("Wrong directory!")
                data_json = json.dumps(data)
                self.sock.sendall(("put|%s" % data_json).encode("utf-8"))
                auth_result = json.loads(self.sock.recv(1024).decode("utf-8"))
                print(auth_result)
                has_sent = 0
                with open(src_file, "rb") as f:
                    if auth_result["result"] == "2003":
                        print(self.response_code_dict["2003"], )
                        print("服务端同名文件大小:%s,本地文件大小:%s" % (auth_result["file_size"], file_size))
                        choice = input("Y:续传;N:覆盖 >>>").strip()
                        if choice.upper() == "Y":
                            f.seek(auth_result["file_size"])
                            self.sock.sendall(b"2004")
                            has_sent += auth_result["file_size"]
                        elif choice.upper() == "N":
                            self.sock.sendall(b"301")
                    elif auth_result["result"] == "302":
                        self.sock.sendall(b"301")
                    print(self.sock.recv(1024))
                    for line in f:
                        self.sock.sendall(line)
                        has_sent += len(line)
                        percent = has_sent / file_size * 100
                        sys.stdout.write("
    ")
                        sys.stdout.write("%.2f%% |%s" % (percent, int(percent) * "*"))
                        sys.stdout.flush()
                        time.sleep(0.01)
    
            else:
                print("file does not exist!")
    
        def cmd_get(self, cmd):
            client_path = ""
            data = {
                "file_name": None,
                "client_path": None,
                "file_size": None,
                "result":"300"
            }
            if len(cmd.split()) > 1:
                file_path = cmd.split()[1]
                file_name = os.path.basename(file_path)
                if file_path.startswith(self.username):
                    client_path = file_path
                else:
                    client_path = os.path.join(self.cwd, file_path)
                if len(cmd.split()) == 2:
                    dst_path = os.path.join(settings.USER_HOME, self.cwd, file_name)
                elif len(cmd.split()) == 3:
                    dst_path = cmd.split()[2]
                    if not dst_path.startswith(self.username):
                        dst_path = os.path.join(settings.USER_HOME, self.cwd, dst_path, file_name)
                    else:
                        dst_path = os.path.join(settings.USER_HOME, dst_path, file_name)
                if os.path.exists(dst_path):
                    file_size = os.stat(dst_path).st_size
                    data["file_size"] = file_size
                data["client_path"] = client_path
                data_json = json.dumps(data)
                self.sock.sendall(("get|%s" % data_json).encode("utf-8"))
                server_reply = json.loads(self.sock.recv(1024).decode("utf-8"))
                has_received = 0
                if server_reply["result"]=="2003":
                    choice=input("目标文件已存在,续载Y或全部重新下载N:").strip()
                    if choice.upper() =="Y":
                        data["result"]="2004"
                        data_json = json.dumps(data).encode("utf-8")
                        self.sock.sendall(data_json)
                        has_received += file_size
                        try:
                            os.makedirs(os.path.dirname(dst_path))
                        except OSError:
                            pass
                        with open(dst_path,"ab") as f:
                            while has_received < server_reply["file_size"]:
                                ret = self.sock.recv(1024)
                                f.write(ret)
                                has_received += len(ret)
                                percent = has_received/server_reply["file_size"]*100
                                sys.stdout.write("
    ")
                                sys.stdout.write("%.2f%%"%percent)
                                sys.stdout.flush()
                    elif choice.upper()=="N":
                        data["result"] = "300"
                        data_json = json.dumps(data).encode("utf-8")
                        self.sock.sendall(data_json)
                        try:
                            os.makedirs(os.path.dirname(dst_path))
                        except OSError:
                            pass
                        with open(dst_path, "wb") as f:
                            while has_received < server_reply["file_size"]:
                                ret = self.sock.recv(1024)
                                f.write(ret)
                                has_received += len(ret)
                                percent = has_received / server_reply["file_size"] * 100
                                sys.stdout.write("
    ")
                                sys.stdout.write("%.2f%%" % percent)
                                sys.stdout.flush()
                elif server_reply["result"]=="300":
                    data["result"] = "300"
                    data_json = json.dumps(data).encode("utf-8")
                    self.sock.sendall(data_json)
                    try:
                        os.makedirs(os.path.dirname(dst_path))
                    except OSError:
                        pass
                    with open(dst_path, "wb") as f:
                        while has_received < server_reply["file_size"]:
                            ret = self.sock.recv(1024)
                            f.write(ret)
                            has_received += len(ret)
                            percent = has_received / server_reply["file_size"] * 100
                            sys.stdout.write("
    ")
                            sys.stdout.write("%.2f%%" % percent)
                            sys.stdout.flush()
    
    
            else:
                print("Wrong instructions")
    
        def cmd_mkdir(self, cmd):
            new_path = ""
            if len(cmd.split()) <= 1:
                pass
            elif len(cmd.split()) == 2:
                input_path = cmd.split()[1]
    
                if input_path.startswith(self.cwd):
                    new_path = input_path
                else:
                    new_path = os.path.join(self.cwd, input_path)
            print("new_path>>>", new_path)
            msg = json.dumps({"cwd": new_path})
            self.sock.sendall(("makedirs|{0}".format(msg)).encode("utf-8"))
            auth_result = json.loads(self.sock.recv(1024).decode("utf-8"))
            if auth_result["result"] == "2003":
                print(self.response_code_dict["2003"])
            else:
                print(self.response_code_dict["2005"])
    
        def cmd_exit(self, cmd):
            self.logout_flag = True
    

    2.3 FTPServer端脚本

    2.3.1 bin目录下的FTPServer.py模块

    import os
    import sys
    sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    from lib import main
    
    if __name__ == '__main__':
        main.ArgvHandler(sys.argv)
    

    2.3.2 config目录下的settings.py模块

    import os
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    USER_HOME = os.path.join(BASE_DIR, "db", "users")
    HOST_IP = "127.0.0.1"
    HOST_PORT = 9999
    USER_ACCOUNT_DIR = os.path.join(BASE_DIR, "db", "user_account")
    

    2.3.3 lib目录下的main.py模块与ftp_server.py模块

    main.py模块

    import os, sys
    
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    import socketserver
    import ftp_server
    from config import settings
    
    
    class ArgvHandler(object):
        def __init__(self, sys_argv):
            self.args = sys_argv
            self.argv_handle()
    
        def argv_handle(self):
            '''
            处理命令行参数,看是否符合输入规范
            :return:
            '''
            if len(self.args) < 2:
                self.help_msg()
            else:
                first_argv = self.args[1]  # first_argv = "start"
                if hasattr(self, first_argv):
                    # 通过反射判断现有类的对象是否有start方法
                    func = getattr(self, first_argv)
                    # 有则通过反射拿到此方法,并运行此方法
                    func()
                else:
                    self.help_msg()
    
        def help_msg(self):
            msg = """
            input like below:
    
            python FTPServer start
            """
    
        def start(self):
            """
            创建多线程socket对象,并让该对象一直运行
            :return:
            """
            try:
                print("starting")
                tcp_server = socketserver.ThreadingTCPServer((settings.HOST_IP, settings.HOST_PORT,),ftp_server.MyServer)
                print("server started")
                tcp_server.serve_forever()
            except KeyboardInterrupt:
                pass
    

    ftp_server.py模块

    import os
    import sys
    
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    
    import subprocess
    import json
    import socketserver
    from config import settings
    
    
    class MyServer(socketserver.BaseRequestHandler):
        response_code_dict = {
            '100': 'user successfully registered',
            '101': 'username already existed',
            '200': 'Pass authentication!',
            '201': 'Wrong username or password!',
            '202': 'user does not exist',
            '300': 'Ready to send file to client',
            '301': 'Client ready to receive file',
            '302': "File doesn't exist",
            '2002': 'ACK(可以开始上传)',
            '2003': 'already existed',
            '2004': 'continue put',
            "2005": "directory created"
        }
    
        def handle(self):
            while True:
                # 死循环,一直接收客户端发过来的消息
                data = self.request.recv(1024).decode()
                if not data:
                    # 如果用户发过来的消息是空,则判定用户断开连接
                    break
                data = data.split("|")
                func_str = data[0]
    
                # 通过反射查看对象有无此属性
                if hasattr(self, func_str):
                    func = getattr(self, func_str)
                    func(json.loads(data[1]))
                else:
                    print("Invalid instructions")
    
        def user_auth(self, name_pwd):
            username = name_pwd["username"]
            password = name_pwd["password"]
            auth_result = {
                "username": username,
                "password": password,
                "storage_size": 0,
                "storage_used": 0,
                "result": "201",
                "user_home": ""
            }
            with open(settings.USER_ACCOUNT_DIR, "r", encoding="utf-8") as f:
                user_info = json.load(f)
                if username in user_info.keys():
                    if password == user_info[username]["password"]:
                        self.login_user = username
                        path = os.path.join(settings.USER_HOME, username)
                        try:
                            os.makedirs(path)
                        except OSError:
                            pass
                        self.login_user_home = os.path.join(self.login_user) + os.sep
                        auth_result["user_home"] = self.login_user_home
                        auth_result["result"] = "200"
                        auth_result["storage_limit"] = user_info[username]["storage_limit"]
                        auth_result["storage_used"] = self.getdirsize(path)
                else:
                    auth_result["result"] = "202"
            data = json.dumps(auth_result).encode("utf-8")
            self.request.sendall(data)
    
        def user_register(self, name_pwd):
            with open(settings.USER_ACCOUNT_DIR, "r", encoding="utf-8") as f:
                user_info = json.load(f)
                if name_pwd["username"] in user_info:
                    self.request.sendall(b"101")
                else:
                    name_pwd["storage_limit"] = 104857600
                    user_info[name_pwd["username"]] = name_pwd
                    self.request.sendall(b"100")
            with open(settings.USER_ACCOUNT_DIR, "w", encoding="utf-8") as f:
                json.dump(user_info, f)
    
        def cd(self, dir_str):
            new_path = dir_str["cwd"]
            # print(new_path)
            server_path = settings.USER_HOME + os.sep + new_path
            # print(server_path)
            auth_result = {"result": "602"}
            if os.path.exists(server_path):
                auth_result["result"] = "601"
            auth_result_json = json.dumps(auth_result)
            self.request.sendall(auth_result_json.encode("utf-8"))
    
        def ls(self, ins):
            dir_str = ins["cwd"]
            server_path = os.sep.join([settings.USER_HOME, dir_str])
            auth_result = {"result": None}
            if os.path.exists(server_path):
                path = os.path.join(settings.USER_HOME, server_path)
                if sys.platform == "win32":
                    command = "dir" + " " + path
                else:
                    command = "ls" + " " + path
                auth_result["result"] = subprocess.getoutput(command)
            msg_size=len(json.dumps(auth_result).encode("utf-8"))
            self.request.sendall(str(msg_size).encode("utf-8"))
            self.request.recv(1024)
            self.request.sendall(json.dumps(auth_result).encode("utf-8"))
    
        def put(self, data):
            file_size = data["file_size"]
            file_name = data["file_name"]
            dst_path = data["dst_path"]
            file_path = os.path.join(settings.USER_HOME, dst_path, file_name)
            # print(file_path)
            if os.path.exists(file_path):
                file_size2 = os.stat(file_path).st_size
                if file_size2 <=file_size:
                    data["file_size"] = file_size2
                data["result"] = "2003"
    
            else:
                data["result"] = "302"
            print(data)
            data_json = json.dumps(data)
            self.request.sendall(data_json.encode("utf-8"))
            client_msg = self.request.recv(1024).decode("utf-8")
            has_received = 0
            if client_msg == "2004":
                has_received += file_size2
                with open(file_path, "ab") as f:
                    self.request.sendall(b"2002")
                    while has_received < file_size:
                        data1 = self.request.recv(1024)
                        f.write(data1)
                        has_received += len(data1)
    
            elif client_msg =="301":
                try:
                    os.makedirs(os.path.dirname(file_path))
                except OSError:
                    pass
                with open(file_path, "wb") as f:
                    self.request.sendall(b"2002")
                    while has_received < file_size:
                        data1 = self.request.recv(1024)
                        f.write(data1)
                        has_received += len(data1)
    
        def get(self, data):
            client_path = data["client_path"]
            print("client_path>>>",client_path)
            file_size = data["file_size"]
            data["result"]="2003"
            server_path = os.path.join(settings.USER_HOME,client_path)
            if os.path.isfile(server_path):
                file_size2 = os.stat(server_path).st_size
                if not file_size:
                    data["result"]="300"
                data["file_size"] = file_size2
                data_json = json.dumps(data).encode("utf-8")
                self.request.sendall(data_json)
                client_reply = json.loads(self.request.recv(1024).decode("utf-8"))
                with open(server_path,"rb") as f :
                    if client_reply["result"] == "2004":
                        f.seek(file_size)
                    for line in f:
                        self.request.sendall(line)
    
    
        def getdirsize(self, path):
            file_size = 0
            for root, dirs, files in os.walk(path):
                file_size += sum([os.path.getsize(os.path.join(root, name)) for name in files])
            return file_size
    
        def makedirs(self, data):
            path = data["cwd"]
            server_path = os.path.join(settings.USER_HOME, path)
            print(server_path)
            auth_result = {"result": None}
            if os.path.exists(server_path):
                auth_result["result"] = "2003"
            else:
                os.makedirs(server_path)
                auth_result["result"] = "2005"
            msg = json.dumps(auth_result).encode("utf-8")
            self.request.sendall(msg)
    
  • 相关阅读:
    【struts2】【2】添加interceptor出错
    C++本质:类的赋值运算符=的重载,以及深拷贝和浅拷贝
    Garbage Collection Essence.Net中Garbage Collection深入探讨
    数据结构C#实现二叉查找树的创建,查找,以及各种递归(非递归)遍历算法
    C#中不安全代码的编写和指针的应用
    C#中的安全策略
    系统诊断概述如何通过windbg来dump特定process的memory.
    经典数据结构之栈的应用迷宫问题
    CPU Scheduling进程调度算法
    ASP.NET中将检索出的数据写入Exel形成Report的一种solution
  • 原文地址:https://www.cnblogs.com/lanxing0422/p/pythonday18.html
Copyright © 2020-2023  润新知