博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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

Posted on 2019-10-31 17:56  bluestarpin  阅读(131)  评论(0编辑  收藏  举报

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:\n
        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("\r")
                    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("\r")
                            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("\r")
                            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("\r")
                        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:\n
        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)