python day 18: thinking in UML与FTP作业重写
Posted on 2019-10-31 17:56 bluestarpin 阅读(132) 评论(0) 编辑 收藏 举报python day 18
2019/10/29
目录
1. thinking in UML读书小感
这3天在看谭云杰的thinking in UML这本书,500多页的PDF扫描版,现在只看到279页,算是看完了一半,很多概念都是半懂不懂的,如在云山雾罩中一样。虽然看得不太明白,但是也有一些小感悟。
- 代码并不是全部,前期的需求分析,建模设计才是重点,这个就像行军打仗做好作战方略,备好粮草一样,后面的代码就是排兵步阵了。
- UML能看懂,不代表会画,会画的人必定是懂得RUP的人,这也解释了我这个初学者连一个小小的多用户登录FTP的程序的用例图都没画好的原因。
- 目标问题,我的目标是学会python,先掌握一门语言,而不是先上来就更高级的系统分析,有点好高骛远了。
- 不过,这本书看到,然后现在停下来,还是对我有不小的收获,对于整个软件开发的全貌有了不同的认识。同时也理解为什么很多公司不愿意招培训班或者自学的人了,因为如果只会写代码,像一些沟通用图如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)