day09 网络编程 Eva-J 博客地址: https://www.cnblogs.com/Eva-J/articles/11437710.html
day09 FTP作业需求地址: https://www.cnblogs.com/Eva-J/articles/7642557.html
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 # Author :王刘俊 4 5 import socketserver 6 import json 7 import hashlib 8 import os, sys 9 import struct 10 11 remote_file = os.path.join(os.path.dirname(__file__), 'remote') # 存储服务端存放文件的绝对路径 12 13 14 class Auth: 15 16 @classmethod 17 def register(cls, usr_dic): # 服务端注册校验结果 18 pwd = cls.get_md5(usr_dic) 19 with open('userinfo', 'a', encoding='utf-8') as f: 20 f.write('%s|%s\n' % (usr_dic['username'], pwd)) 21 result_dic = {'operate': 'register', 'flag': True} 22 return result_dic 23 24 @classmethod 25 def login(cls, usr_dic): # 服务端登录校验结果 26 pwd = cls.get_md5(usr_dic) 27 with open('userinfo', encoding='utf-8') as f: 28 for line in f: 29 if line: 30 userinfo = line.strip().split('|') 31 if userinfo[0] == usr_dic['username'] and userinfo[1] == pwd: 32 result_dic = {'operate': 'login', 'flag': True} 33 break 34 else: 35 result_dic = {'operate': 'login', 'flag': False} 36 return result_dic 37 38 @staticmethod 39 def get_md5(usr_dic): # 根据用户名加盐对密码进行md5加密 40 md5 = hashlib.md5(usr_dic['username'].encode('utf-8')) 41 md5.update(usr_dic['password'].encode('utf-8')) 42 pwd = md5.hexdigest() 43 return pwd 44 45 @staticmethod 46 def myquit(opt_dic): # 退出 47 opt_dic['flag'] = False 48 return opt_dic 49 50 51 class Myserver(socketserver.BaseRequestHandler): 52 def mysend(self, dic, protocol=False): # 发送信息 53 str_d = json.dumps(dic).encode('utf-8') 54 if protocol: 55 len_b = len(str_d) # 计算json格式的字典长度 56 len_dic = struct.pack('i', len_b) # 将长度利用struct转化为4个字节 57 self.request.send(len_dic) # 发送长度 58 self.request.send(str_d) 59 60 def myrecv(self, msg_len=1024, protocol=False): # 自定义协议接收数据 61 if protocol: 62 bytes_len = self.request.recv(4) # 接收4字节,即将接受字符串的长度 63 msg_len = struct.unpack('i', bytes_len)[0] # 解析字符串长度 64 msg = self.request.recv(msg_len) # 根据指定长度接收信息 65 str_msg = msg.decode('utf-8') # 解码 66 opt_dic = json.loads(str_msg) 67 return opt_dic 68 69 @staticmethod 70 def processBar(num, total): # 实现进度条 71 rate = num / total 72 rate_num = int(rate * 100) 73 if rate_num == 100: 74 r = '\r%s>%d%%\n' % ('=' * rate_num, rate_num,) 75 else: 76 r = '\r%s>%d%%' % ('=' * rate_num, rate_num,) 77 sys.stdout.write(r) 78 sys.stdout.flush 79 80 def upload(self, opt_dic): # 上传文件 81 filename = opt_dic['filename'] 82 file_path = os.path.join(remote_file, filename) 83 # 按指定大小依次接收文件内容,并写入新文件 84 with open(file_path, 'wb') as f: 85 filesize = opt_dic['filesize'] 86 while filesize > 0: 87 content = self.request.recv(10240) 88 f.write(content) 89 filesize -= len(content) 90 self.processBar(opt_dic['filesize'] - filesize, opt_dic['filesize']) 91 print('已成功发送\033[34m%s\033[0m,共计\033[34m%s\033[0m字节' % (filename, opt_dic['filesize'])) 92 opt_dic['flag'] = True 93 return opt_dic 94 95 def download(self, opt_dic): # 下载文件 96 # 将remote 文件夹下的文件名和大小写入字典,准备发送到客户端 97 file_dic = {} 98 for file in os.listdir(remote_file): 99 file_path = os.path.join(remote_file, file) 100 file_dic[file] = os.path.getsize(file_path) 101 self.mysend(file_dic, True) 102 dic = self.myrecv(protocol=True) 103 file_path = os.path.join(remote_file, dic['filename']) 104 with open(file_path, 'rb') as f: 105 filesize = dic['filesize'] 106 while filesize > 0: 107 content = f.read(10240) 108 self.request.send(content) 109 filesize -= len(content) 110 self.processBar(dic['filesize'] - filesize, dic['filesize']) 111 print('已发送文件\033[34m%s\033[0m 共计\033[34m%s\033[0m字节' % (dic['filename'], dic['filesize'])) 112 opt_dic['flag'] = True 113 return opt_dic 114 115 def myquit(self, opt_dic): # 退出 116 opt_dic['flag'] = False 117 return opt_dic 118 119 def handle(self): # 重写handle方法 120 while True: 121 try: 122 usr_dic = self.myrecv(protocol=True) # 接收指令 123 if hasattr(Auth, usr_dic['operate']): # 反射注册、登录验证 124 result_dic = getattr(Auth, usr_dic['operate'])(usr_dic) 125 if result_dic['flag'] and usr_dic['operate'] != 'myquit': 126 self.mysend(result_dic, protocol=True) 127 while result_dic['flag']: 128 opt_dic = self.myrecv(protocol=True) 129 if hasattr(Myserver, opt_dic['operate']): # 反射上传、下载 130 result_dic = getattr(Myserver, opt_dic['operate'])(self, opt_dic) 131 if not result_dic['flag']: break 132 print('客户端%s已断开连接' % self.client_address[0]) 133 break 134 except ConnectionResetError: 135 print('客户端%s已断开连接:' % self.client_address[0]) 136 break 137 138 139 sk = socketserver.ThreadingTCPServer(('127.0.0.1', 9000), Myserver) 140 sk.serve_forever()FTP-server
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 # Author :王刘俊 4 5 import socket 6 import json 7 import os, sys 8 import struct 9 10 local_file = os.path.join(os.path.dirname(__file__), 'local') # 存储客户端存放文件的绝对路径 11 12 13 def send_dic(sk, dic, protocol=False): # 自定义发送信息函数 14 bytes_d = json.dumps(dic).encode('utf-8') # 字典转为json格式并编码 15 if protocol: 16 len_b = len(bytes_d) # 计算json格式的字典长度 17 len_dic = struct.pack('i', len_b) # 将长度利用struct转化为4个字节 18 sk.send(len_dic) # 发送长度 19 sk.send(bytes_d) 20 21 22 def recv_dic(sk, msg_len=1024, protocol=False): 23 if protocol: 24 bytes_len = sk.recv(4) # 接收4字节,即将接受字符串的长度 25 msg_len = struct.unpack('i', bytes_len)[0] # 解析字符串长度 26 msg = sk.recv(msg_len) # 根据指定长度接收信息 27 str_msg = msg.decode('utf-8') # 解码 28 opt_dic = json.loads(str_msg) 29 return opt_dic 30 31 32 def get_usr(opt='login'): # 接收输入的用户名密码 33 usr_dic = {} 34 inp_usr = input('请输入用户名:').strip() 35 inp_pwd = input('请输入密码:').strip() 36 if inp_usr and inp_pwd and opt == 'register': 37 cfm_pwd = input('再次确认密码:').strip() 38 if inp_pwd == cfm_pwd: # 发送明文密码 39 usr_dic = {'username': inp_usr, 'password': inp_pwd, 'operate': opt} 40 elif inp_usr and inp_pwd: 41 usr_dic = {'username': inp_usr, 'password': inp_pwd, 'operate': opt} 42 return usr_dic 43 44 45 def login(sk): # 登录 46 print('in login') 47 ret = get_usr() 48 if ret: send_dic(sk, ret, protocol=True) 49 res_dic = recv_dic(sk, protocol=True) 50 if res_dic['operate'] == 'login' and res_dic['flag']: 51 print('登录成功') 52 else: 53 print('登录失败') 54 return res_dic['flag'] 55 56 57 def register(sk): # 注册 58 print('in register') 59 ret = get_usr('register') 60 if ret: send_dic(sk, ret, protocol=True) 61 res_dic = recv_dic(sk, protocol=True) 62 if res_dic['operate'] == 'register' and res_dic['flag']: 63 print('注册成功') 64 else: 65 print('注册失败') 66 return res_dic['flag'] 67 68 69 def upload(sk): # 文件上传 70 path = input('请输入要上传的文件路径:').strip() 71 if os.path.isfile(path): # 确保文件是真实存在的 72 # 拿到文件名字和大小 73 filename = os.path.basename(path) 74 filesize = os.path.getsize(path) 75 dic = {'filename': filename, 'filesize': filesize, 'operate': 'upload'} 76 send_dic(sk, dic, protocol=True) 77 # 按指定字节大小发送文件 78 with open(path, 'rb') as f: 79 al_size = filesize # 记录下载进度 80 while al_size > 0: 81 content = f.read(10240) 82 sk.send(content) 83 al_size -= len(content) 84 processBar(filesize - al_size, filesize) 85 print('文件\033[34m%s\033[0m已成功上传' % filename) 86 return True 87 88 89 def download(sk): # 文件下载 90 dic = {'operate': 'download'} 91 send_dic(sk, dic, protocol=True) # 发送download 指令 92 file_dic = recv_dic(sk, protocol=True) # 接收文件列表 93 print('-' * 30) 94 print('序号\t文件名\t文件大小') 95 for index, file in enumerate(file_dic, 1): 96 print('%s %s\t%s' % (index, file, file_dic[file])) 97 print('-' * 30) 98 choice = input('请输入要下载的文件名:').strip() 99 if choice in file_dic and file_dic[choice]: # 所选文件存在且大小不为0 100 dic = {'filename': choice, 'filesize': file_dic[choice], 'operate': 'download'} 101 send_dic(sk, dic, protocol=True) 102 # 按指定字节大小接收文件到local目录 103 file_path = os.path.join(local_file, choice) 104 with open(file_path, 'wb') as f: 105 filesize = dic['filesize'] 106 while filesize > 0: 107 content = sk.recv(10240) 108 f.write(content) 109 filesize -= len(content) # 发送大小 110 processBar(dic['filesize'] - filesize, dic['filesize']) 111 print('文件\033[34m%s\033[0m已下载至\033[34m%s\033[0m' % (dic['filename'], local_file)) 112 else: 113 print('文件不存在') 114 return True 115 116 117 def processBar(num, total): # 实现进度条 118 rate = num / total 119 rate_num = int(rate * 100) 120 if rate_num == 100: 121 r = '\r%s>%d%%\n' % ('=' * rate_num, rate_num,) 122 else: 123 r = '\r%s>%d%%' % ('=' * rate_num, rate_num,) 124 sys.stdout.write(r) 125 sys.stdout.flush 126 127 128 def myquit(sk): 129 dic = {'operate': 'myquit'} 130 send_dic(sk, dic, protocol=True) 131 print('已断开连接') 132 return False 133 134 135 def choose_opt(opt_lst): # 选择菜单 136 print('-' * 30) 137 for index, opt in enumerate(opt_lst, 1): 138 print(index, opt[0]) 139 print('-' * 30) 140 while True: 141 try: 142 num = int(input('请输入要选择的操作序号:')) 143 return opt_lst[num - 1][1] 144 except (ValueError, IndexError): 145 print('输入不合法') 146 147 148 sk = socket.socket() 149 sk.connect(('127.0.0.1', 9000)) 150 151 opt_lst = [('登录', login), ('注册', register), ('退出', myquit)] 152 func = choose_opt(opt_lst) 153 res = func(sk) # 登录注册 154 while res: 155 opt_lst2 = [('上传', upload), ('下载', download), ('退出', myquit)] 156 func = choose_opt(opt_lst2) 157 res = func(sk) # 登录注册 158 if not res: break 159 sk.close()FTP-client
remote 为服务端存放文件目录, local 为客户端存放文件目录
已完成:
1. 多用户同时登陆
2. 用户登陆,加密认证
3.传输过程中现实进度条
4.客户端意外输出、退出服务端异常处理
待完善:
1. 完整度md5校验
2. 磁盘分配: 每个用户拥有自己独立的目录
3. 删除文件和文件夹,新建、上传文件夹
4.待补充