##########总结###########
文件校验加进度条显示
####server import os import json import socket import struct import hashlib sk = socket.socket() sk.bind(('127.0.0.1', 9000)) sk.listen() conn, addr = sk.accept() filename = 'mysql-5.6.42-winx64.zip' # 文件名 absolute_path = os.path.join('D:\老男孩老师笔记\第二阶段共享', filename) # 文件绝对路径 buffer_size = 1024 * 1024 # 缓冲大小,这里表示1MB md5obj = hashlib.md5() with open(absolute_path, 'rb') as f: while True: content = f.read(buffer_size) # 每次读取指定字节 if content: md5obj.update(content) else: break # 当内容为空时,终止循环 md5 = md5obj.hexdigest()#打印出16进制的md5值 print(md5) # 打印md5值 dic = {'filename': filename, 'filename_md5': str(md5), 'buffer_size': buffer_size, 'filesize': os.path.getsize(absolute_path)} str_dic = json.dumps(dic).encode('utf-8') len_dic = len(str_dic) length = struct.pack('i', len_dic) conn.send(length) # dic的长度 conn.send(str_dic) # dic with open(absolute_path, 'rb') as f: # 文件 while dic['filesize']: content = f.read(dic['buffer_size']) conn.send(content) dic['filesize'] -= len(content) ''' 这里不能减等4096,因为文件,最后可能只有3字节。 要根据读取的长度len(content),来计算才是合理的。 ''' conn.close() ############### 1cb1926af121c5c1b52a1ec13314805b
####client import json import struct import socket import sys import time import hashlib import os def processBar(num, total): # 进度条 [接收大小,文件大小] rate = num / total rate_num = int(rate * 100) if rate_num == 100: r = '\r%s>%d%%\n' % ('=' * rate_num, rate_num,) else: r = '\r%s>%d%%' % ('=' * rate_num, rate_num,) sys.stdout.write(r) sys.stdout.flush start_time = time.time() # 开始时间 sk = socket.socket() sk.connect(('127.0.0.1', 9000)) dic_len = sk.recv(4) dic_len = struct.unpack('i', dic_len)[0]#查看长度 str_dic = sk.recv(dic_len).decode('utf-8') dic = json.loads(str_dic) md5 = hashlib.md5() with open(dic['filename'], 'wb') as f: # 使用wb更严谨一些,虽然可以使用ab content_size = 0 while True: content = sk.recv(dic['buffer_size']) # 接收指定大小 f.write(content) # 写入文件 content_size += len(content) # 接收大小 md5.update(content) # 摘要 processBar(content_size, dic['filesize']) # 执行进度条函数 [接收大小,文件大小] if content_size == dic['filesize']: break # 当接收的等于文件大小时,终止循环 md5 = md5.hexdigest() print(md5) # 打印md5值 if dic['filename_md5'] == str(md5): print(('md5校验正确--下载成功')) else: print('文件验证失败') os.remove(dic['filename']) # 删除文件 sk.close() # 关闭连接 end_time = time.time() # 结束时间 print('本次下载花费了{}秒'.format(end_time - start_time)) ############ ====================================================================================================>100% 1cb1926af121c5c1b52a1ec13314805b md5校验正确--下载成功 本次下载花费了18.044031858444214秒
####验证合法性
使用hashlib.md5 加密
为什么要随机字符串,是为了防止客户端的数据被窃取
生成随机的bytes类型数据,它是解不出来的
import os print(os.urandom(32))
执行输出:
b'PO\xca8\xc8\xf3\xa0\xb5,\xdd\xb8K \xa8D\x9cN"\x82\x03\x86g\x18e\xa7\x97\xa77\xb9\xa5VA'
###server import os import socket import hashlib secret_key = '老衲洗头用飘柔' # 加密key sk = socket.socket() sk.bind(('127.0.0.1',9000)) sk.listen() while True: try: conn,addr = sk.accept() random_bytes = os.urandom(32) # 随即产生32个字节的字符串,返回bytes conn.send(random_bytes) # 发送随机加密key md5 = hashlib.md5(secret_key.encode('utf-8')) # 使用secret_key作为加密盐 md5.update(random_bytes) #得到MD5消息摘要 ret = md5.hexdigest() #以16进制返回消息摘要,它是一个32位长度的字符串 msg = conn.recv(1024).decode('utf-8') # 接收的信息解码 if msg == ret:print('是合法的客户端') # 如果接收的摘要和本机计算的摘要一致,就说明是合法的 else:conn.close() # 关闭连接 finally: # 无论如何,都执行下面的代码 sk.close() # 关闭连接 break
###client import socket import hashlib secret_key = '老衲洗头用飘柔' # 加密key sk = socket.socket() sk.connect(('127.0.0.1',9000)) urandom = sk.recv(32) # 接收32字节,也就是os.urandom的返回值 md5_obj = hashlib.md5(secret_key.encode('utf-8')) # 使用加密盐加密 md5_obj.update(urandom) sk.send(md5_obj.hexdigest().encode('utf-8')) # 发送md5摘要 print('-----') sk.close() # 关闭连接
先执行server.py,再执行client.py
client输出:-----
server输出:是合法的客户端
socketserver
SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进 程” 专门负责处理当前客户端的所有请求。
它能实现多个客户端,同时连接,它继承了socket
####server import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): print(self.request) self.request.send(b'hello') # 跟所有的client打招呼 print(self.request.recv(1024)) # 接收客户端的信息 server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer) server.serve_forever()
#######client import socket sk = socket.socket() sk.connect(('127.0.0.1',9000)) print(sk.recv(1024)) inp = input('>>>').encode('utf-8') sk.send(inp) sk.close()
client输出:
b'hello'
>>>hi
server输出:
b'hi'
开多个客户端,也可以执行
########自己写一个socketserver
###server import socket from threading import Thread class MySocket: #init方法,将ip和端口封装到了self里面,并且实例化了一个socket对象 def __init__(self,server_addr): self.server_addr = server_addr self.socket = socket.socket() #绑定了ip地址和端口,监听,执行了建立连接的方法 def serve_forever(self): self.socket.bind(self.server_addr) self.socket.listen(5) self.build_connect() #建立连接,将建立连接的过程循环起来,因为我们每次建立连接都需要开一个线程带走这个连接,去通过这个连接收发消息. def build_connect(self): while 1: #让主线程代码循环起来,要让accept方法和handle方法要异步起来,不然建立连接的这个循环无法循环起来,因为进入handle之后就出不来了,异步你会想到什么,并发,多进程或者多线程,多进程不合适,所以用多线程,每一个conn,一个线程 conn,addr = self.socket.accept() # self.handle(conn) #开线程处理conn,一个线程一个conn, t = Thread(target=self.handle,args=(conn,)) t.start() #收发消息的内容,每个线程都需要执行一下handle方法,将conn作为参数传给handle方法 def handle(self,conn): while 1: from_client_msg = conn.recv(1024).decode('utf-8') print('来自客户的消息>>>>',from_client_msg) to_client_msg = input('宝宝说>>>>') conn.send(to_client_msg.encode('utf-8')) if __name__ == '__main__': ip_port = ('127.0.0.1',8001) server = MySocket(ip_port) server.serve_forever()
###client 可实现多个客户端 同时和一个server应对 import socket client = socket.socket() client.connect(('127.0.0.1',8001)) while 1: to_server_msg = input('给宝宝的消息>>>') client.send(to_server_msg.encode('utf-8')) from_server_msg = client.recv(1024).decode('utf-8') print('来自宝宝的消息:',from_server_msg)