开发一个可以多个用户在线的FTP程序
要求:
1.用户加密认证 (完成) 操作前需要进行登录 需要登录与md5验证 2.允许同时多用户登录 (完成) 使用socketserver模块进行实现 3.每个用户都有自己的家目录,且只能访问自己的家目录 (完成) 每注册一个就创建一个文件夹 4.对用户进行磁盘配额,每个用户的可用空间不同 (完成) 配置文件,计算自己家目录里的所有文件大小进行比较 当上传文件的时候进行判断存储空间是否足够,不够将不进行传输 5.允许用户在ftp server 上随意切换目录 (完成) 切换目录中还有一个功能:在当前目录下进行创建目录 只能在自己的文件夹下进行切换 只支持两种方式: 1.纯点的路径:cd ... 2.纯路径形的:cd heak\ket 6.允许用户查看当前目录下文件 (完成) 查看当前目录下的文件与文件夹 查看方式: 1.dir a 2.dir heat\hes 7.允许上传和下载文件,保证文件一致性 (完成) 传输与上传文件的时候,用hashlib做,每传输一次update一次, 最后将客户端加密的结果发给服务端进行校验,校验失败提示传输异常 8.文件传输过程中显示进度条 (完成) 百分百精度条 9.附加功能:支持文件的断点续传 (完成) 当用户上传或下载的时候,先在另一端的目录中进行查询是否有该文件, 有:判断文件大小进行确定是否是完整文件,如果是完整文件进行重新传输并覆盖, 如果不是完整文件就移动光标至断点处进行传输 没有:直接进行传输
配置:
服务端:
seeting:
用户配置文件:
server_start:
import os import sys sys.path.append(os.path.dirname(os.path.dirname(__file__))) from care.server_main import Mysocket from conf.setting import Ip_address import socketserver if __name__ =='__main__': sever = socketserver.ThreadingTCPServer(Ip_address, Mysocket) sever.serve_forever()
1 import os 2 import json 3 import struct 4 import configparser 5 import subprocess 6 import re 7 import hashlib 8 import socketserver 9 10 11 from conf.setting import user_info 12 13 class Mysocket(socketserver.BaseRequestHandler): 14 15 16 17 def handle(self): 18 self.start = True 19 self.user_login = {'user_name': '', 'login': False} 20 self.allow_reuse_address = True 21 self.coding = 'utf-8' 22 self.Max_accept = 8092 23 self.Not_modified_server_dir = r"D:\flin\网络编程\FtpProject\Server\db" 24 self.server_dir = r"D:\flin\网络编程\FtpProject\Server\db" 25 self.operation_list = [{'登录': 'login'}, {'注册': 'registered'}] 26 27 while self.start: 28 try: 29 if not self.user_login["login"]: # 判断是否是已登录状态 30 head_login = self.user_login 31 head_login_json = json.dumps(head_login).encode(self.coding) 32 head_login_size = struct.pack('i', len(head_login_json)) 33 34 self.request.send(head_login_size) # 发送报头长度 35 self.request.send(head_login_json) # 发送报头 36 37 user_head_size = self.request.recv(4) 38 user_head_unpk = struct.unpack('i', user_head_size)[0] 39 40 user_head = self.request.recv(user_head_unpk).decode(self.coding) 41 42 user_head_json = json.loads(user_head) # 反序列化拿到报头 43 user_name = user_head_json['user_name'] 44 password = user_head_json['password'] 45 options_index = user_head_json['options'] - 1 46 47 options = list(self.operation_list[options_index].values())[0] 48 49 # ================登录 / 注册======================= 50 if hasattr(self, options): 51 func = getattr(self, options) 52 options_info = func(user_name, password) 53 54 if not options_info: 55 continue 56 57 self.server_dir = os.path.join(self.server_dir, self.user_login['user_name']) 58 59 # =========================以下为命令处理部分=================== 60 while self.start: 61 conter_inp = self.request.recv(self.Max_accept).decode(self.coding) 62 if not conter_inp: 63 self.start = False 64 break 65 conter = conter_inp.split() 66 head = conter[0] 67 file_path = conter[1] 68 69 if hasattr(self, head): 70 func = getattr(self, head) 71 ret = func(file_path) 72 if not ret: 73 continue 74 75 except Exception as e: 76 continue 77 78 self.request.close() 79 self.user_login = {'user_name': '', 'login': False} 80 self.server_dir = r"D:\flin\网络编程\FtpProject\Server\db" 81 def put(self, file_path): 82 try: 83 head_size = self.request.recv(4) 84 head_size_unpk = struct.unpack('i', head_size)[0] 85 head_top = self.request.recv(head_size_unpk) # 接收头部 86 head_json = json.loads(head_top.decode(self.coding)) 87 88 file_name = os.path.basename(head_json['file_path']) 89 file_size = head_json['file_size'] 90 91 file_name_path = os.path.join(self.server_dir, file_name) # 当前目录加上文件名 92 93 94 95 # 计算该用户的所有文件总大小 96 obj = configparser.ConfigParser() 97 obj.read(user_info, encoding=self.coding) 98 file_dir_size = self.cal_file_size(os.path.join(self.Not_modified_server_dir, 99 self.user_login['user_name'])) # 计算用户的总大小 100 101 user_dir = os.listdir(self.server_dir) # 获取当前文件夹下的所有文件名 102 hex_update = self.md5() 103 if file_name in user_dir: # 目录下已有该文件 104 self.request.send(b'1') 105 has_file_size = os.stat(file_name_path).st_size # 获取已有文件的大小 106 self.request.sendall(bytes(str(has_file_size), encoding=self.coding)) 107 108 define_has_size = int(self.request.recv(1).decode(self.coding)) # 接收是否是完整文件大小,完整接收1,不完整接收0 109 110 if define_has_size == 1: 111 has_file_size = 0 112 with open(file_name_path, 'wb') as f: 113 f.seek(has_file_size) 114 while has_file_size < file_size: 115 data = self.request.recv(1024) 116 f.write(data) 117 hex_update.update(data) 118 has_file_size += len(data) 119 ret = hex_update.hexdigest() 120 client_update = self.request.recv(1024).decode(self.coding) 121 if ret != client_update: 122 self.request.sendall(b'0') 123 return 124 125 else: 126 self.request.sendall(b'1') 127 return 128 129 130 elif define_has_size == 0: 131 with open(file_name_path, 'ab') as f: 132 f.seek(has_file_size) 133 while has_file_size < file_size: 134 data = self.request.recv(1024) 135 f.write(data) 136 hex_update.update(data) 137 has_file_size += len(data) 138 ret = hex_update.hexdigest() 139 client_update = self.request.recv(1024).decode(self.coding) 140 if ret != client_update: 141 self.request.sendall(b'0') 142 return 143 144 else: 145 self.request.sendall(b'1') 146 return 147 148 else: 149 self.request.send(b'0') # 没有文件 150 if file_dir_size + file_size > int(obj[self.user_login["user_name"]]['capacity']): # 判断该用户空间是否足够 151 self.request.send(b'0') 152 return False 153 self.request.send(b'1') # 空间够 154 number = 0 155 with open(file_name_path, 'wb') as f: 156 while number < file_size: 157 line = self.request.recv(1024) 158 f.write(line) 159 hex_update.update(line) 160 number += len(line) 161 print("文件总大小:%s 已上传 %s" % (file_size, number)) 162 ret = hex_update.hexdigest() 163 client_update = self.request.recv(1024).decode(self.coding) 164 if ret != client_update: 165 self.request.sendall(b'0') 166 return 167 168 else: 169 self.request.sendall(b'1') 170 return 171 172 173 except Exception as e: 174 print(e.__traceback__.tb_lineno) 175 176 return False 177 178 def get(self, file_path): 179 """下载文件 180 只能是文件名的命令:kite.py 181 不能是路径,只能是文件名 182 支持断点续传 183 1.验证接收到的路径是否有效,若无效:返回0, 有效:返回1 184 2.发送报头长度,发送报头 185 186 3.接收发来的文件的大小,与原文件进行比较,相等或小于源文件:调整光标 进行发送 187 接收到'no': 从0开始发送 188 4.发送完毕""" 189 try: 190 file_path = os.path.join(self.server_dir, os.path.basename(file_path)) 191 if not os.path.exists(file_path) and not os.path.isfile(file_path): # 判断文件是否有效 192 self.request.send(b'0') 193 return False 194 self.request.send(b'1') 195 196 # 拿到文件的大小 197 file_size = os.path.getsize(file_path) 198 199 # 制作报头 200 head_top = {'file_path': file_path, 'file_size': file_size, 201 } 202 # 发送报头的长度信息 203 head_json = json.dumps(head_top) 204 head_bytes = head_json.encode(self.coding) 205 head_size = struct.pack('i', len(head_bytes)) 206 207 self.request.send(head_size) 208 self.request.send(head_json.encode(self.coding)) 209 210 211 # ==================================== 212 define_size = self.request.recv(1).decode(self.coding) 213 # 接收是否有该文件,有为1,没有为0 214 obj = self.md5() 215 if define_size: 216 has_file_size = int(self.request.recv(1024).decode(self.coding)) 217 if has_file_size == file_size: 218 has_file_size = 0 219 220 with open(file_path, 'rb') as f: 221 f.seek(has_file_size) 222 while has_file_size < file_size: 223 data = f.read(1024) 224 self.request.sendall(data) 225 obj.update(data) 226 has_file_size += len(data) 227 print("文件总大小:%s 已下载 %s" % (file_size, has_file_size)) 228 ret = obj.hexdigest() 229 client_update = self.request.recv(1024).decode(self.coding) 230 if ret != client_update: 231 self.request.sendall(b'0') 232 return 233 234 else: 235 self.request.sendall(b'1') 236 return 237 238 239 240 else: 241 number = 0 242 with open(file_path, 'rb') as f: 243 f.seek(0) 244 for line in f: 245 self.request.send(line) 246 obj.update(line) 247 number += len(line) 248 print("文件总大小:%s 已下载 %s" % (file_size, number)) 249 ret = obj.hexdigest() 250 client_update = self.request.recv(1024).decode(self.coding) 251 if ret != client_update: 252 self.request.sendall(b'0') 253 return 254 255 else: 256 self.request.sendall(b'1') 257 return 258 259 except Exception as e: 260 print(e.__traceback__.tb_lineno) 261 262 def md5(self): 263 hash_lib = hashlib.md5(bytes('key', encoding='utf-8')) 264 return hash_lib 265 266 def cd(self, file_path): 267 """切换路径 268 1.带点的切换 :... ,只能用'.',后面还不能加目录名 269 2.不带点的:heat\keta 270 """ 271 file_path_pattern = re.search('^\.+(\w+)?$', file_path) 272 if file_path_pattern: 273 number = file_path.count('.') # 有几个'.'就循环几次 274 count = 0 275 res = '' 276 source_path = self.server_dir 277 while count < number: 278 res = os.path.dirname(source_path) 279 source_path = res 280 if res == self.Not_modified_server_dir: 281 return False 282 count += 1 283 self.server_dir = res 284 self.request.send(b'1') 285 286 287 else: 288 dir_path = os.path.join(self.server_dir, file_path) 289 dir_path_list = [] 290 ret = os.walk(self.server_dir) 291 # 拿到当前目录下的所有所有目录的路径 292 for item in ret: 293 for sub_dir in item[1]: 294 s = os.path.join(item[0], sub_dir) 295 dir_path_list.append(s) 296 if dir_path in dir_path_list: 297 self.server_dir = dir_path # 跟换当前路径 298 self.request.send(b'1') 299 return 300 self.request.send(b'0') 301 302 def mk(self, file_path): 303 """在当前目录下创建目录""" 304 if file_path not in os.listdir(self.server_dir): 305 dir_path = os.path.join(self.server_dir, os.path.basename(file_path)) 306 os.makedirs(dir_path) 307 self.request.send(b'1') 308 self.request.send(b'0') 309 310 def dir(self, file_path): 311 """查看目录下的文件夹与文件的名字 312 1.查看当前目录:dir a 313 2.查看其他目录:dir heat\hes 314 """ 315 316 if file_path == 'a': 317 obj = subprocess.Popen('dir' + ' ' + self.server_dir, shell=True, 318 stdout=subprocess.PIPE, 319 stderr=subprocess.PIPE) 320 321 stdout = obj.stdout.read() 322 stderr = obj.stdout.read() 323 324 stdout_size = struct.pack('i', len(stdout) + len(stderr)) 325 326 self.request.send(stdout_size) # 发送长度 327 328 self.request.send(stdout) 329 self.request.send(stderr) 330 return 331 else: 332 if os.path.isdir(file_path) and os.path.exists(os.path.join(self.server_dir, file_path)): 333 obj = subprocess.Popen('dir' + ' ' + os.path.join(self.server_dir, file_path), shell=True, 334 stderr=subprocess.PIPE, 335 stdout=subprocess.PIPE) 336 337 stdout = obj.stdout.read() 338 stderr = obj.stdout.read() 339 340 stdout_size = struct.pack('i', len(stdout) + len(stderr)) 341 342 self.request.send(stdout_size) 343 self.request.send(stdout) 344 self.request.send(stderr) 345 return 346 else: 347 return False 348 349 def cal_file_size(self, path): 350 """计算用户文件夹总大小""" 351 number = 0 352 ret = os.listdir(path) 353 for i in ret: 354 s = os.path.join(path, i) 355 if os.path.isdir(s): 356 number = self.cal_file_size(s) + number 357 elif os.path.isfile(s): 358 file_size = os.path.getsize(s) 359 number += file_size 360 return number 361 362 def login(self,user_name,password): 363 """登录>>>>验证用户名密码,这里有个坑,读写文件的时候一定要加上编码 364 成功,发送1 365 失败,发送0 366 """ 367 obj = configparser.ConfigParser() 368 obj.read(user_info,encoding=self.coding) 369 if user_name in obj.sections() and password == obj[user_name]['password']: 370 self.user_login['user_name'] = user_name 371 self.user_login['login'] = True 372 self.request.send(b'1') 373 return True 374 else: 375 self.request.send(b'0') 376 return False 377 378 def registered(self,user_name,password): 379 """注册>>>>验证用户名是否重复 380 重复,发送0 381 不重复,发送1,创建用户信息写入文件 382 """ 383 obj = configparser.ConfigParser() 384 obj.read(user_info,encoding=self.coding) 385 if user_name in obj.sections(): 386 387 self.request.send(b'0') 388 return False 389 elif user_name not in obj.sections(): 390 self.request.send(b'1') 391 # 392 os.makedirs(os.path.join(self.server_dir, user_name)) 393 obj[user_name]={'password':password, 394 'capacity':1024000, 395 'user_filedir':os.path.join(self.server_dir, user_name)} 396 with open(user_info,'w',encoding=self.coding) as f: 397 obj.write(f) 398 399 return False
客户端:
配置:
setting:
start:
import os import sys sys.path.append(os.path.dirname(os.path.dirname(__file__))) from conf.setting import Ip_address from care.client_main import Client if __name__ =='__main__': obj = Client(Ip_address) obj.run()
1 import os 2 import struct 3 import json 4 from care.user_login import user_record 5 from conf.setting import user_room 6 import hashlib 7 import socket 8 9 10 class Client: 11 address_family = socket.AF_INET 12 socket_type = socket.SOCK_STREAM 13 user_login = {'user_name': None, 'login': False} 14 operation_list = [{'登录': 'login'}, {'注册': 'registered'}] 15 coding = 'utf-8' 16 Max_accept = 8092 17 Client_dir = user_room # 默认下载目录 18 19 def __init__(self, client_address, connect_and_active=True): 20 self.client = client_address 21 self.socket = socket.socket(self.address_family, self.socket_type) 22 self.attom = False 23 24 25 if connect_and_active: 26 try: 27 print('正在连接服务端…………') 28 self.client_connect() 29 print('连接成功') 30 print(self.client[0]) 31 print(self.client[1]) 32 except Exception: 33 self.client_close() 34 raise 35 36 def client_connect(self): 37 """进行链接""" 38 self.socket.connect(self.client) 39 40 def client_close(self): 41 """关闭客户端""" 42 self.socket.close() 43 44 def run(self): 45 """主程序""" 46 47 print('———————————————————欢迎来到FTP文件上传下载中心————————————————————') 48 49 while 1: # 判断是否已登录,若未登录需登录 50 self.cot = self.socket.getsockname() # 获取本机IP与port 51 self.cat = self.socket.getpeername() # 获取远程发送数据的IP与port 52 heard_login_size = self.socket.recv(4) 53 54 heard_login_size_unpk = struct.unpack('i',heard_login_size)[0] 55 heard_login = self.socket.recv(heard_login_size_unpk).decode(self.coding) 56 57 heard_login_json = json.loads(heard_login) 58 user_login = heard_login_json['login'] 59 60 if not user_login: # 如果为False 61 user_name,password,number = user_record(self.attom) 62 63 user_heard = {'user_name':user_name,'password':password,'options':number} 64 user_heard_json = json.dumps(user_heard).encode(self.coding) 65 user_heard_size = struct.pack('i',len(user_heard_json)) 66 67 self.socket.send(user_heard_size) 68 self.socket.send(user_heard_json) 69 70 71 if number == 1: # 如果是登录 72 self.attom = number # 改变attom的值 73 ret = self.socket.recv(1).decode(self.coding) # 接收服务端发来的账号密码验证 74 if not int(ret): 75 print("用户名或密码错误,请重新输入") 76 continue 77 print('登录成功') 78 79 elif number == 2: 80 ret = self.socket.recv(1).decode(self.coding) 81 if not int(ret): 82 print("用户名已被注册,请重新输入一个用户名") 83 self.attom = number 84 continue 85 print('注册成功') 86 continue 87 # =====================以下为输入命令部分======================= 88 while 1: 89 user_inpt = input('<<<').strip() 90 if user_inpt=='exit()': 91 return 92 if not user_inpt: # 为空跳过 93 continue 94 try: 95 client_cent = user_inpt.split() 96 client_heard = client_cent[0] 97 client_file = client_cent[1] 98 if hasattr(self, client_heard): 99 100 func = getattr(self, client_heard) 101 res = func(user_inpt, client_file) 102 if not res: 103 continue 104 105 except Exception as e : 106 107 print(e.__traceback__.tb_lineno) 108 print("请输入正确的命令") 109 continue 110 111 112 def get(self, args, client_file): 113 """下载文件,先发送用户输入的命令在服务端进行验证, 114 服务端会发送验证后的信息,如果文件存在就进行传输,不行就重新输入 115 断点续传:当文件传输还未传输完时中断了, 116 当用户再次登录进行输入命令进行下载的时候,会在未传输完整的文件上继续传输完 117 如果文件已完整存在就会进行重新传输,并覆盖原文件 118 如果文件不存在就从头开始传输 119 1.先发送命令,接收服务端发来的文件路径是否有效 120 2.接收报头长度,接收报 121 3.判断下载目录中是否已有该文件,已有:发送1 122 没有该文件:发送0 123 4.如果是文件是完整的会重新传输并覆盖,如不完整:调整光标到断点进行写入 124 如没有将从0开始进行传输""" 125 126 try: 127 self.socket.send(args.encode(self.coding)) # 发送整条命令 128 129 Validation_path = self.socket.recv(1).decode(self.coding) # 接收是否是有效路径 130 131 if not int(Validation_path): 132 print('文件路径无效,请输入正确的文件路径') 133 return False 134 135 head_size = self.socket.recv(self.Max_accept) # 接收报头长度 136 head_size_unpk = struct.unpack('i', head_size) 137 head_top = self.socket.recv(head_size_unpk[0]) # 接收报头 138 139 head_json = json.loads(head_top) 140 file_size = head_json['file_size'] 141 file_path = head_json['file_path'] 142 143 144 # ======================以下为传输部分====================== 145 file_name = os.path.basename(file_path) 146 file = os.path.join(self.Client_dir,file_name) # 将当前目录路径与文件名进行合并 147 download_dir = os.listdir(self.Client_dir) # 拿到下载目录下的所有文件名 148 md5_update = self.md5() 149 150 if file_name in download_dir: 151 self.socket.send(b'1') 152 has_file_size = os.stat(file).st_size # 拿到已有文件的大小 153 self.socket.sendall(bytes(str(has_file_size), encoding=self.coding)) # 发送文件大小 154 if has_file_size==file_size: 155 has_file_size = 0 156 with open(file,'wb') as f: 157 f.seek(has_file_size) 158 while has_file_size<file_size: 159 data = self.socket.recv(1024) 160 f.write(data) 161 md5_update.update(data) 162 has_file_size+=len(data) 163 res = '\r %.2f %s ' 164 print(res % ((has_file_size / file_size) * 100, 165 int((has_file_size / file_size) * 100) * '*'), 166 end='') 167 168 ret = md5_update.hexdigest() 169 self.socket.sendall(bytes(ret,encoding=self.coding)) 170 res = self.socket.recv(1024).decode(self.coding) 171 if res: 172 print('') 173 print('下载完成') 174 else: 175 print('下载异常,请删除后重新下载') 176 177 elif has_file_size<file_size: 178 with open(file,'ab') as f: 179 f.seek(has_file_size) # 移动光标至断点 180 print("断点续传") 181 while has_file_size<file_size: 182 line = self.socket.recv(1024) 183 f.write(line) 184 md5_update.update(line) 185 has_file_size+=len(line) 186 res = '\r %.2f %s ' 187 print(res % ((has_file_size / file_size) * 100, int((has_file_size / file_size) * 100) * '*'), end='') 188 189 190 ret = md5_update.hexdigest() 191 self.socket.sendall(bytes(ret, encoding=self.coding)) 192 res = self.socket.recv(1024).decode(self.coding) 193 if res: 194 print('') 195 print('下载完成') 196 else: 197 print('下载异常,请删除后重新下载') 198 else: 199 self.socket.send(b'0') # 发送没有该文件的信息 200 201 self.socket.sendall(b'0') 202 with open(file, 'wb') as f: 203 f.seek(0) 204 number = 0 205 while number < file_size: 206 ret = self.socket.recv(1024) 207 f.write(ret) 208 md5_update.update(ret) 209 number += len(ret) 210 res = '\r %.2f %s ' 211 print(res % ((number / file_size) * 100, int((number / file_size) * 100) * '*'), end='') 212 213 214 ret = md5_update.hexdigest() 215 self.socket.sendall(bytes(ret, encoding=self.coding)) 216 res = self.socket.recv(1024).decode(self.coding) 217 if res: 218 print('') 219 print('下载完成') 220 else: 221 print('下载异常,请删除后重新下载') 222 223 except Exception as e: 224 print(e.__traceback__.tb_lineno,e.__traceback__.tb_next) 225 226 227 def md5(self): 228 """ 229 数据传输时加密,传输完成并返回给服务端进行验证 230 :return: 返回一个对象 231 """ 232 hash_lib = hashlib.md5(bytes('key',encoding=self.coding)) 233 return hash_lib 234 235 def put(self, args, client_file): 236 """ 237 上传文件,不限制文件路径 238 :param args: 用户输入的命令 239 :param client_file: 文件路径 240 :return: 241 """ 242 try: 243 if not os.path.exists(client_file) and not os.path.isfile(client_file): # 判断是否是有效路径与是否是一个文件 244 print('未找到文件,请输入正确且有效的文件路径') 245 return 246 247 self.socket.send(args.encode(self.coding)) # 发送指令 248 249 file_size = os.path.getsize(client_file) # 获取要上传的文件的大小 250 heard_top = {'file_path': client_file, 251 'file_size': file_size, 252 253 } 254 heard_json = json.dumps(heard_top) # 序列化 255 heard_bytes = heard_json.encode(self.coding) 256 heard_size = struct.pack('i', len(heard_bytes)) # 打包报头的长度 257 258 self.socket.send(heard_size) # 发送报头的长度 259 self.socket.send(heard_bytes) # 发送报头 260 261 262 263 # =========================以下为传输部分,含断点续传======================= 264 mode = int(self.socket.recv(1).decode(self.coding)) # 接收 是否已有文件 有接收1 没有接收0 265 md5_update = self.md5() 266 if mode==1: 267 has_file_size = int(self.socket.recv(1024).decode(self.coding)) # 接收已有的文件大小 268 if has_file_size==file_size: # 已有文件大小与要上传的文件的大小一致,重新上传将其覆盖 269 self.socket.send(b'1') # 发送信息告知服务端 270 has_file_size = 0 # 从0开始进行发送 271 with open(client_file, 'rb') as f: 272 print('已有文件重新传输') 273 print(has_file_size, type(has_file_size)) 274 f.seek(has_file_size) 275 while has_file_size < file_size: 276 data = f.read(1024) 277 self.socket.send(data) 278 md5_update.update(data) 279 has_file_size += len(data) 280 res = '\r %.2f %s ' 281 print(res % ((has_file_size / file_size) * 100, int((has_file_size / file_size) * 100) * '*'), end='') 282 283 ret = md5_update.hexdigest() 284 self.socket.sendall(bytes(ret, encoding=self.coding)) 285 res = self.socket.recv(1024).decode(self.coding) 286 if res : 287 288 print('') 289 print('上传完成') 290 else: 291 print('传输异常') 292 293 else: 294 self.socket.send(b'0') 295 # 告知服务端不是完整文件,移动光标至断点处进行读取,发送 296 with open(client_file, 'rb') as f: 297 print('断点续传') 298 print(has_file_size, type(has_file_size)) 299 f.seek(has_file_size) 300 while has_file_size < file_size: 301 data = f.read(1024) 302 self.socket.send(data) 303 md5_update.update(data) 304 has_file_size += len(data) 305 res = '\r %.2f %s ' 306 print(res % ((has_file_size / file_size) * 100, 307 int((has_file_size / file_size) * 100) * '*'), end='') 308 309 310 ret = md5_update.hexdigest() 311 self.socket.sendall(bytes(ret, encoding=self.coding)) 312 res = self.socket.recv(1024).decode(self.coding) 313 if res: 314 315 print('') 316 print('上传完成') 317 else: 318 print('传输异常') 319 320 elif mode==0: 321 # 没有该文件,先接收服务端发来的储存空间是否足够,足够进行传输 322 defind_capacity = self.socket.recv(1).decode(self.coding) 323 if defind_capacity==0: 324 print('......空间不足,无法进行传输') 325 return False 326 print('正在传输,请等待……') 327 number = 0 328 res = '\r %.2f %s ' 329 with open(client_file, 'rb') as f: 330 for line in f: 331 self.socket.send(line) 332 md5_update.update(line) 333 number += len(line) 334 print(res % ((number / file_size) * 100, int((number / file_size) * 100) * '*'), end='') 335 336 337 ret = md5_update.hexdigest() 338 self.socket.sendall(bytes(ret, encoding=self.coding)) 339 res = self.socket.recv(1024).decode(self.coding) 340 if res: 341 342 print('') 343 print('上传完成') 344 else: 345 print('传输异常') 346 except Exception as e: 347 print(e.__traceback__.tb_lineno,e.__traceback__.tb_lasti) 348 349 350 351 def dir(self,args, client_file): 352 """查看当前目录中的所有文件与文件夹名""" 353 354 self.socket.send(args.encode(self.coding)) 355 356 stdout_size = self.socket.recv(4) 357 stdout_size_unpk = struct.unpack('i',stdout_size)[0] 358 359 number = 0 360 conter = b'' 361 # 接收结果并打印 362 while number<stdout_size_unpk: 363 line = self.socket.recv(1024) 364 conter+=line 365 number+=len(line) 366 print(conter.decode('GBK')) 367 368 def mk(self,args,client_file): 369 """在当前目录下创建文件夹""" 370 self.socket.send(args.encode(self.coding)) # 发送命令 371 ret = self.socket.recv(1).decode(self.coding) 372 if ret: 373 print('目录创建成功') 374 return 375 print('目录已存在') 376 377 378 def cd(self,args,client_file): 379 """切换目录""" 380 self.socket.send(args.encode(self.coding)) # 发送命令 381 ret = self.socket.recv(1).decode(self.coding) 382 if ret: 383 print('目录切换成功') 384 return 385 print('目录切换失败,请输入正确的路径') 386 if __name__ == '__main__': 387 obj = Client(('127.0.0.1', 8002)) 388 obj.run()
user_login:
import re import hashlib class Operate: """用户输入,返回用户名与密码 密码用md5进行加密 """ def encryption(self,password): """MD5密码加密""" obj = hashlib.md5(bytes('key', encoding='utf-8')) obj.update(bytes(password, encoding='utf-8')) password = obj.hexdigest() return password def login(self,number): """登录""" while 1: print('登录>>>>>>>>>>>') user_name = input("请输入用户名").strip() if user_name.upper()=='Q': return False password = input("请输入密码").strip() if password.upper()=='Q': return False user_pattern = re.search('^\w{6,}$', user_name) password_pattern = re.search('^[0-9A-Za-z_@]{8,}$', password) if user_pattern and password_pattern: password = self.encryption(password) return user_name,password,number else: print("请输入正确的用户密码") def registered(self,number): """注册""" while 1: print('注册>>>>>>>>>>>') user_name = input("请输入您要注册的用户名:(至少6位数字或字母)").strip() if user_name.upper()=='Q': return False password = input("请输入您的密码: (至少8位数字或字母)").strip() if user_name.upper()=='Q': return False user_pattern = re.search('^\w{6,}$',user_name) password_pattern = re.search('^[0-9A-Za-z_@]{8,}$',password) if user_pattern and password_pattern: password = self.encryption(password) return user_name,password,number else: print("用户名或密码不合法请重新输入") def user_record(attom): """循环选择选项,返回用户名与密码 这里传入attom,默认是False,需要进行输入序号 一旦输入序号后出现了输入错误,就不会再需要输入序号了,是直接进行循环输入""" while 1: operation_list = [{'登录': 'login'}, {'注册': 'registered'}] if not attom: print('请选择您要继续的操作:') for index, operation in enumerate(operation_list, 1): print(index, list(operation.keys())[0]) try: user_inp = input('请输入序号:') if user_inp.upper() == 'Q': return False user_int = int(user_inp) # 拿到索引字典中的value operation_list_value = operation_list[user_int - 1][list(operation_list[user_int - 1])[0]] # 反射到类里面进行执行 if hasattr(Operate, operation_list_value): func_operation = getattr(Operate(), operation_list_value) ret = func_operation(int(user_int)) # 传入数字 if not ret: continue return ret except Exception as e: print("请输入正确的序号") continue else: operation_list_value = operation_list[attom - 1][list(operation_list[attom - 1])[0]] # 拿到索引字典中的value if hasattr(Operate, operation_list_value): func_operation = getattr(Operate(), operation_list_value) ret = func_operation(attom) return ret