项目: 更新(二) python 实现大概FTP的功能
服务器利用 socketserver 模块 构造, 实现了 多进程.
客户端仍然利用的是底层的 socket模块. 只不过进行了更深度的 解耦, 新加或者删除 某些功能 更方便
在上一个版本的基础上,增加了 新建文件夹的功能, 并且将vip用户的名字加上了颜色, 变成了黄金 vip.
服务器端设置了两个文件, 1, info( 存储用户密码); 2, vipinfo (存储所有的vip用户的名字)
还需要再补充一个log文件夹,里面用来保存所有客户的访问记录. ps: 此功能还没添加.
下面是代码:
服务器端:
1 import socketserver 2 import struct 3 import pickle 4 import os 5 6 7 class Myserver(socketserver.BaseRequestHandler): 8 def __init__(self, request, client_address, server): 9 self.dic_cho = { 10 'login': self.login, # 登录 11 'register': self.register, # 注册 12 'new_file': self.new_file, # 创建新的文件夹 13 'down_dir': self.down_dir, # 进入下一层 14 'up_dir': self.up_dir, # 进入上一层 15 'upload': self.upload, # 上传 16 'download': self.download, # 下载 17 'vip': self.vip, # 升级vip 18 'end': self.end, # 结束 19 'size': 102400 20 } 21 self.flag = False 22 self.flag_end = True 23 super().__init__(request, client_address, server) 24 25 def my_reve(self): # 因为每次接收后的内容都会转成字典, 所以重写了接收的函数 26 return pickle.loads(self.request.recv(struct.unpack('i', self.request.recv(4))[0])) 27 28 def my_send(self, dic): # 重写了 发送方法. 29 dic_pic = pickle.dumps(dic) 30 return self.request.send(struct.pack('i', len(dic_pic)) + dic_pic) 31 32 def isvip(self, server_dict, client_dic): 33 """此函数是 login函数的附属函数, 只是用来判断该用户是否为Vip 34 35 :param server_dict: 服务器需要发送的字典 36 :param client_dic: 服务器接收的客户端发来的字典 37 :return: None 38 """ 39 with open('vipinfo', encoding='utf-8') as f: 40 for name in f: 41 if client_dic['username'] == name.strip(): 42 self.flag = True 43 server_dict['return'] = '登录成功, 欢迎尊敬的VIP用户:\033[1;33;m%s\033[0m 访问ftp' % client_dic['username'] 44 self.dic_cho['size'] = 10240000 45 server_dict['size'] = '容量:(单位:字节)%s/10240000' % os.path.getsize(self.dir_path) 46 return 47 48 def login(self, client_dic, server_dict): 49 self.dir_path = os.path.join(os.path.dirname(__file__), client_dic['username']) 50 # 第一步 判断用户名+密码 是否正确 51 with open('info', encoding='utf-8') as f: 52 for line in f: 53 if client_dic['username'] + '\t' + client_dic['password'] == line.strip(): 54 server_dict['return'] = '登录成功, 欢迎%s 用户访问ftp' % client_dic['username'] 55 server_dict['size'] = f'容量:(单位:字节){os.path.getsize(self.dir_path)}/102400' 56 server_dict['dir_list'] = os.listdir(self.dir_path) 57 # 第二步 判断此人是不是会员, 有没有vip文件中 58 self.isvip(server_dict, client_dic) 59 # 第三步, 将字典发送给 客户端并且 结束函数 60 self.my_send(server_dict) 61 return 62 server_dict['return'] = '错误:输入错误' 63 self.my_send(server_dict) 64 65 def register(self, client_dic, server_dict): 66 self.dir_path = os.path.join(os.path.dirname(__file__), client_dic['username']) 67 with open('info', encoding='utf-8') as f: 68 for line in f: 69 username, password = line.strip().split('\t') 70 if client_dic['username'] == username: 71 server_dict['return'] = '错误:用户名已存在' 72 self.my_send(server_dict) 73 return 74 with open('info', 'a+', encoding='utf-8') as f: 75 f.write(client_dic['username'] + '\t' + client_dic['password'] + '\n') 76 self.flag = True 77 server_dict['return'] = '注册成功,自动登录中' 78 os.mkdir(client_dic['username']) 79 server_dict['dir_list'] = os.listdir(self.dir_path) 80 server_dict['size'] = f'容量:(单位:字节){os.path.getsize(self.dir_path)}/102400' 81 self.my_send(server_dict) 82 83 def new_file(self, client_dic, server_dict): 84 """ 85 此函数和下面的六个函数 被执行的前提是客户已经登录成功了, 86 且已经在客户端显示出来了文件列表, 若没有登录, 该用户访问不到此方法 87 88 此函数的功能是 在当前工作列表中新建文件夹 89 :param client_dic: 90 :param server_dict: 91 :return: 92 """ 93 new_name = client_dic['new_dir_name'] 94 os.mkdir(os.path.join(self.dir_path, new_name)) 95 server_dict['return'] = '%s 文件夹新建成功' % new_name 96 self.my_send(server_dict) 97 98 def down_dir(self, client_dic, server_dict): 99 down_dir_name = client_dic['down_dir_name'] 100 self.dir_path = os.path.join(self.dir_path, down_dir_name) 101 server_dict['dir_list'] = os.listdir(self.dir_path) 102 self.my_send(server_dict) 103 104 def up_dir(self, client_dic, server_dict): 105 dir_path_if = os.path.dirname(self.dir_path) 106 if dir_path_if == os.path.dirname(__file__): 107 server_dict['return'] = '已经是最上层目录了' 108 else: 109 self.dir_path = dir_path_if 110 server_dict['dir_list'] = os.listdir(self.dir_path) 111 self.my_send(server_dict) 112 113 def upload(self, client_dic, server_dict): 114 filesize = client_dic['upload_file_size'] 115 size = os.path.getsize(self.dir_path) 116 if filesize+size >= self.dic_cho['size']: 117 server_dict['return'] = '内存不足' 118 self.my_send(server_dict) 119 return 120 filename = os.path.join(self.dir_path, client_dic['upload_file_name']) 121 with open(filename, 'wb') as f: 122 while filesize: 123 if filesize < 1024: 124 content = self.request.recv(filesize) 125 else: 126 content = self.request.recv(1024) 127 f.write(content) 128 filesize -= len(content) 129 server_dict['return'] = '上传成功' 130 server_dict['dir_list'] = os.listdir(self.dir_path) 131 self.my_send(server_dict) 132 133 def download(self, client_dic, server_dict): 134 filename = os.path.join(self.dir_path, client_dic['download_file_name']) 135 filesize = os.path.getsize(filename) 136 server_dict['file_size'] = filesize 137 self.my_send(server_dict) 138 with open(filename, 'rb') as f: 139 while filesize: 140 if filesize > 1024: 141 content = f.read(filesize) 142 else: 143 content = f.read(1024) 144 self.request.send(content) 145 filesize -= len(content) 146 ok = self.request.recv(2) 147 print(ok) 148 self.request.send(b'download ok') if ok == b'ok' else self.request.send(b'download no') 149 150 def vip(self, client_dic, server_dict): 151 with open('vipinfo', 'r+', encoding='utf-8') as f: 152 for line in f: 153 if client_dic['username'] == line.strip(): 154 server_dict['return'] = '您已经是尊敬的Vip用户了' 155 self.my_send(server_dict) 156 return 157 f.seek(0, 2) 158 f.write(client_dic['username'] + '\n') 159 self.isvip(server_dict, client_dic) 160 self.my_send(server_dict) 161 162 def end(self, client_dic, server_dict): 163 self.flag_end = False 164 server_dict['return'] = '程序结束' 165 self.my_send(server_dict) 166 167 def handle(self): 168 server_dict = {} 169 flag = self.flag_end 170 while flag: 171 try: 172 client_dic = self.my_reve() 173 self.dic_cho[client_dic['opt']](client_dic, server_dict) 174 except Exception:pass 175 176 177 server = socketserver.TCPServer(('127.0.0.1', 9090), Myserver) 178 server.serve_forever()
客户端:
1 import os 2 import socket 3 import struct 4 import pickle 5 import hashlib 6 7 8 class Myclient(socket.socket): 9 def __init__(self): 10 super().__init__() 11 self.dic_cho = { 12 'login': self.login, # 登录 13 'regis': self.register, # 注册 14 'new_file': self.new_file, # 创建新的文件夹 15 'down_dir': self.down_dir, # 进入下一层 16 'up_dir': self.up_dir, # 进入上一层 17 'upload': self.upload, # 上传 18 'download': self.download, # 下载 19 'vip': self.vip, # 升级vip 20 'end': self.end, # 结束 21 } 22 self.flag = True 23 24 def my_reve(self): 25 return pickle.loads(self.sk.recv(struct.unpack('i', self.sk.recv(4))[0])) 26 27 def my_send(self, dic): 28 dic_pic = pickle.dumps(dic) 29 return self.sk.send(struct.pack('i', len(dic_pic)) + dic_pic) 30 31 def login(self):pass 32 33 def register(self):pass 34 35 def my_md5(self, user, pwd): 36 md5_obj = hashlib.md5(user.encode('utf-8')) 37 md5_obj.update(pwd.encode('utf-8')) 38 return md5_obj.hexdigest() 39 40 def new_file(self): 41 new_file_name = input('>>>请输入新建文件夹的名字:').strip() 42 self.client_dict['new_dir_name'] = new_file_name 43 self.my_send(self.client_dict) 44 server_dict = self.my_reve() 45 print(server_dict['dir_list']) 46 47 def down_dir(self): 48 down_dir_name = input('>>>请输入下一级文件夹的名字:').strip() 49 self.client_dict['down_dir_name'] = down_dir_name 50 self.my_send(self.client_dict) 51 server_dict = self.my_reve() 52 print(server_dict['dir_list']) 53 54 def up_dir(self): 55 self.my_send(self.client_dict) 56 server_dict = self.my_reve() 57 print(server_dict['dir_list']) 58 59 def upload(self): 60 upload_file_path = input('>>>请输入上传文件绝对路径:').strip() 61 self.client_dict['upload_file_name'] = os.path.basename(upload_file_path) 62 self.client_dict['upload_file_size'] = os.path.getsize(upload_file_path) 63 filesize = self.client_dict['upload_file_size'] 64 self.my_send(self.client_dict) 65 with open(upload_file_path, 'rb') as f: 66 while filesize: 67 if filesize > 1024: 68 content = f.read(filesize) 69 else: 70 content = f.read(1024) 71 self.sk.send(content) 72 filesize -= len(content) 73 server_dict = self.my_reve() 74 print(server_dict['return']) 75 print(server_dict['dir_list']) 76 77 def download(self): 78 download_file_name = input('>>>请输入您要下载的文件的名字:').strip() 79 download_file_path = input('>>>请输入要下载到本地的绝对路径以及对该文件命名:').strip() 80 self.client_dict['download_file_name'] = download_file_name 81 self.my_send(self.client_dict) 82 server_dict = self.my_reve() 83 filesize = server_dict['file_size'] 84 with open(download_file_path, 'wb') as f: 85 while filesize: 86 if filesize < 1024: 87 content = self.sk.recv(filesize) 88 else: 89 content = self.sk.recv(1024) 90 f.write(content) 91 filesize -= len(content) 92 self.sk.send(b'ok') 93 ok = self.sk.recv(11) 94 if ok == b'download ok': 95 print('下载成功') 96 else: 97 print('下载失败') 98 print(server_dict['dir_list']) 99 100 def vip(self): 101 self.my_send(self.client_dict) 102 server_dict = self.my_reve() 103 print(server_dict['return']) 104 print(server_dict['size']) 105 print(server_dict['dir_list']) 106 107 def end(self): 108 self.my_send(self.client_dict) 109 self.flag = False 110 server_dict = self.my_reve() 111 print(server_dict['return']) 112 113 def main(self): 114 main_dict = { 115 '1': self.new_file, 116 '2': self.down_dir, 117 '3': self.up_dir, 118 '4': self.upload, 119 '5': self.download, 120 '6': self.vip, 121 '7': self.end, 122 } 123 while 1: 124 print('1, 在当前目录新建文件夹\n' 125 '2, 进入下级目录\n' 126 '3, 返回上级目录\n' 127 '4, 上传文件\n' 128 '5, 下载文件\n' 129 '6, 升级Vip\n' 130 '7, 退出') 131 cho_main = input('>>>请输入功能选项ID:').strip() 132 try: 133 self.client_dict['opt'] = main_dict[cho_main].__name__ 134 main_dict[cho_main]() 135 if cho_main == '7': 136 break 137 except Exception: 138 print('选项输入不规范') 139 140 def my_input(self, call_dict): 141 print('欢迎来到FTP\n1, 登录\n2, 注册') 142 cho = input('>>>请输入选项ID:').strip() 143 if cho != '1' and cho != '2': 144 return 3 145 username = input('>>>Username:').strip() 146 password = input('>>>Password:').strip() 147 self.client_dict['opt'] = call_dict[cho].__name__ 148 self.client_dict['username'] = username 149 self.client_dict['password'] = self.my_md5(username, password) 150 self.my_send(self.client_dict) 151 152 def main_main(self): 153 call_dict = {'1': self.login, '2': self.register} 154 self.client_dict = {} 155 flag = self.flag 156 while flag: 157 ret = self.my_input(call_dict) 158 if ret == 3: 159 print('输入错误') 160 continue 161 server_dict = self.my_reve() 162 print(server_dict['return']) 163 if server_dict['return'][0] != '错': 164 print(server_dict['size']) 165 print(server_dict['dir_list']) 166 self.main() 167 flag = self.flag 168 169 def __call__(self, *args, **kwargs): 170 self.sk = socket.socket() 171 self.sk.connect(('127.0.0.1', 9090)) 172 self.main_main() 173 self.sk.close() 174 175 176 if __name__ == '__main__': 177 client = Myclient() 178 client() 179 180 # 这个是 发送的字典的内容, 每次操作, 所对应的键值对 中的值都会发生改变. 181 # client_dict = {'opt': None, # 选项 182 # 'username': None, # 用户名 183 # 'password': None, # 密码 184 # 'new_dir_name': None, # 新建文件夹的名字 185 # 'down_dir_name': None, # 打开下一层文件夹的名字 186 # 'upload_file_name': None, # 上传到ftp里面的文件的名字 187 # 'upload_file_size': None, # 上传的文件的大小 188 # 'download_file_name': None, # 下载的文件名字 189 # }