day38作业
1、整理GIL解释器锁,解释以下问题
1、什么是GIL
Global Interpreter Lock 全局解释器锁
2、有了GIL会对单进程下的多个线程造成什么样的影响
使得单进程下的多线程按照串行的方式使用解释器资源。
3、为什么要有GIL
放置线程竞争解释器资源,导致使用出错,也使得解释器的内部数据被多线程修改出现错误。
4、GIL与自定义互斥锁的区别,多个线程争抢GIL与自定义互斥锁的过程分析
全局锁不能保证自己开启的线程安全 但是保证解释器中的数据的安全的
GIL 在线程调用解释器时 自动加锁 在IO阻塞时或线程代码执行完毕时 自动解锁
自定义互斥锁需要自己手动上锁,手动释放。
5、什么时候用python的多线程,什么时候用多进程,为什么?
多线程用于密集io操作的程序,而多进程多用于密集计算的程序当中,因为全局解释器锁对于,单一进程下的多进程使用一个cpu核心,多io操作的程序主要消耗时间为io操作,使用多线程内存消耗小,效率不下于多进程。对于密集计算型程序,使用多进程的每个cpu的核心都能被利用,效率大于单核心的处理。
2、进程池与线程池
1、池的用途,为何要用它
就是一个装进程的容器
2、池子里什么时候装进程什么时候装线程?
密集io操作的程序使用多线程、密集计算的操作使用多进程
3、基于进程池与线程池实现并发的套接字通信
客户端
import socket client = socket.socket() client.connect(("127.0.0.1",3555)) while True: msg = input(">>>") client.send(msg.encode("utf-8")) data = client.recv(1024) print(data.decode("utf-8"))
服务端(进程池和线程池)
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor #导入进程池 import socket def task(client): while True: try: data = client.recv(1024) print(data.decode("utf-8")) if not data: client.close() break client.send(data.upper()) except Exception: print("客户端断开") client.close() break if __name__ == '__main__': server = socket.socket() server.bind(("127.0.0.1",3555)) server.listen(5) #创建进程池默认个数为cpu的核心数 # pool = ProcessPoolExecutor() #创建线程池默认个数为cpu核心的5倍 pool = ThreadPoolExecutor() while True: client,addr = server.accept() pool.submit(task,client)
4、基于线程池实现一个可以支持并发通信的套接字,完成以下功能?
执行客户端程序,用户可选的功能有:
1、登录
2、注册
3、上传
4、下载
思路解析:
1、执行登录,输入用户名egon,密码123,对用户名egon和密码进行hash校验,并加盐处理,将密文密码发送到服务端,与服务端事先存好用户名与密文密码进行对比,对比成功后,
在服务端内存中用hash算法生成一个随机字符串比如eadc05b6c5dda1f8772c4f4ca64db110
然后将该字符串发送给用户以及登录成功的提示信息发送给客户端,然后在服务存放好
current_users={
'a3sc05b6c5dda1f8313c4f4ca64db110':{'uid':0,'username':'alex'},
'e31adfc05b6c5dda1f8772c4f4ca64b0':{'uid':1,'username':'lxx'},
'eadc05b6c5dda1f8772c4f4ca64db110':{'uid':2,'username':'egon'},
}
用户在收到服务端发来的'eadc05b6c5dda1f8772c4f4ca64db110'以及登录成功的提示信息后,以后的任何操作都会携带该随机字符串'eadc05b6c5dda1f8772c4f4ca64db110‘,服务端会根据该字符串获取用户信息来进行与该用户匹配的操作
在用户关闭连接后,服务端会从current_users字典中清除用户信息,下次重新登录,会产生新的随机字符串
这样做的好处:
1、用户的敏感信息全都存放到服务端,更加安全
2、每次登录都拿到一个新的随机的字符串,不容易被伪造
2、执行注册功能,提交到服务端,然后存放到文件中,如果用户已经存在则提示用户已经注册过,要求重新输入用户信息
3、执行上次下载功能时会携带用户的随机字符串到服务端,如果服务端发现该字符串not in current_users,则要求用户先登录
客户端
import socket,struct,json,hashlib,os client = socket.socket() client.connect(("127.0.0.1",4566)) current_code = None def send_data_to_server(data): """ :param client: 连接 :param data: 符合json格式的数据 :return: """ json_data = json.dumps(data) # 在转化可传输的二进制形式 bytes_data = json_data.encode("utf-8") # 计算传输需要的的长度 len_bytes = len(bytes_data) # 将长度整形打包成为二进制位 b_len = struct.pack("i", len_bytes) # 传输数据 client.send(b_len) client.send(bytes_data) def get_recv_data(): """ 接受服务返回的数据 :param client: :return: """ b_len = client.recv(4) # 转换为数字 bytes_len = struct.unpack("i", b_len)[0] recv_data = b"" len_recv = 0 while len_recv < bytes_len: r_data = client.recv(1024) recv_data += r_data len_recv += len(r_data) str_data = recv_data.decode("utf-8") data = json.loads(str_data) return data def encryption(pwd): res = hashlib.md5(pwd.encode("utf-8")) return res.hexdigest() def regist(): """ 注册 :return: """ while True: name = input("请输入你的姓名(q 退出)").strip() if name == "q": return pwd = input("请输入你的密码").strip() if name and pwd: func_id = 1 pwd = encryption(pwd) dic = {"name":name,"pwd":pwd,"func_id":func_id} send_data_to_server(dic) #等待接收服务端的返回 msg_dic = get_recv_data() print(msg_dic["msg"]) if msg_dic["flag"]: return else: print("用户和密码不能为空") def login(): """ 登录,获得连接特征码 :return: """ while True: name = input("请输入你的姓名(q 退出)").strip() if name == "q": return pwd = input("请输入你的密码").strip() if name and pwd: func_id = 1 pwd = encryption(pwd) dic = {"name": name, "pwd": pwd, "func_id": func_id} send_data_to_server(dic) msg_dic =get_recv_data() print(msg_dic["msg"]) if msg_dic["flag"]: global current_code current_code = msg_dic["current_code"] return else: print("用户名和密码不能为空") #上传 def upload(): while True: filename = input("请输入文件路径(q 退出)").strip() if filename == 'q': return if not filename: print("文件名不能为空") continue if os.path.exists(filename): file_name = os.path.split(filename)[1] file_size = os.path.getsize(filename) file_dic = {"file_name":file_name,"file_size":file_size,"current_code":current_code} send_data_to_server(file_dic) with open(filename,"rb") as f: for line in f: client.send(line) msg_dic = get_recv_data() print(msg_dic["msg"]) if msg_dic["flag"]: return else: print("文件路劲不存在") continue #下载 def download(): while True: filename = input("请输入下文件的名字(q 退出)").strip() if filename == "q": return if not filename: print("文件不能为空") continue dic = {"filename":filename} send_data_to_server(dic) file_dic = get_recv_data() print(file_dic["msg"]) if file_dic["flag"]: filename = file_dic["filename"] filesize = file_dic["file_size"] file_path = os.path.join(r"F:\Python_exe\day38\userdb",filename) b_len = 0 with open(file_path,"ab") as f: while b_len<filesize: data = client.recv(1024) f.write(data) b_len += len(data) return func = {"1":regist,"2":login,"3":upload,"4":download} while True: print(""" 1、注册 2、登录 3、上传 4、下载 q 退出 """) index = input("请输入你想要的操作").strip() if index == "q": break if index in func: func[index]() else: print("错误操作")
服务端
from concurrent.futures import ThreadPoolExecutor import socket,struct,json import hashlib import time import os server = socket.socket() server.bind(("127.0.0.1",4566)) server.listen(5) current_user = {} def get_recv_data(client): """ 接受服务返回的数据 :param client: :return: """ b_len = client.recv(4) # 转换为数字 bytes_len = struct.unpack("i", b_len)[0] recv_data = b"" len_recv = 0 while len_recv < bytes_len: r_data = client.recv(1024) recv_data += r_data len_recv += len(r_data) str_data = recv_data.decode("utf-8") data = json.loads(str_data) return data #发送个客户端 def send_data_to_client(client,data): """ :param client: 连接 :param data: 符合json格式的数据 :return: """ json_data = json.dumps(data) # 在转化可传输的二进制形式 bytes_data = json_data.encode("utf-8") # 计算传输需要的的长度 len_bytes = len(bytes_data) # 将长度整形打包成为二进制位 b_len = struct.pack("i", len_bytes) # 传输数据 client.send(b_len) client.send(bytes_data) #用户是否存在 def get_user(name): with open(r"F:\Python_exe\day38\user_db.json","r",encoding="utf-8") as f: users = json.load(f) for user in users: if user["name"] == name: return user #添加用户到文件中去 def save_user_to_file(name,pwd): dic = {"name":name,"pwd":pwd} with open(r"F:\Python_exe\day38\user_db.json","r",encoding="utf-8") as f: users = json.load(f) users.append(dic) with open(r"F:\Python_exe\day38\user_db.json", "w", encoding="utf-8") as f: json.dump(users,f) def regist(client,data): name = data["name"] pwd = data["pwd"] #校验用户是否存在 user = get_user(name) if user: msg = "用户已存在" flag = False else: save_user_to_file(name,pwd) msg = "创建用户成功" flag = True msg_dic = {"msg":msg,"flag":flag} send_data_to_client(client,msg_dic)#发送给客户端 def login(client,data): name = data["name"] pwd = data["pwd"] # 校验用户是否存在 user = get_user(name) if user: if pwd == user["pwd"]: msg = "登录成功" flag = True #生成一个current_code加入current_user,并返回一个current_code给客户端 code = "%s%s%s"%(name,pwd,time.time()) current_code = hashlib.md5(code.encode("utf-8")).hexdigest() current_user[current_code] = {"username":name} msg_dic = {"msg":msg,"flag":flag,"current_code":current_code} else: msg = "密码错误" flag = False msg_dic = {"msg": msg, "flag": flag} else: msg = "用户不存在,请先注册" flag = False msg_dic = {"msg": msg, "flag": flag} send_data_to_client(client,msg_dic) def upload(client,data): if data["current_code"] in current_user: file_name = data["file_name"] file_size = data["file_size"] by_len = 0 with open(r'F:\Python_exe\day38\server_db\%s'%file_name,"ab") as f: while by_len<file_size: data = client.recv(1024) f.write(data) by_len += len(data) msg = "上传成功" flag = True else: msg = "用户认证失败" flag = False msg_dic = {"msg":msg,"flag":flag} send_data_to_client(client,msg_dic) def download(client,data): filename = data["filename"] file_path = os.path.join("F:\Python_exe\day38\server_db",filename) if not os.path.exists(file_path): msg = "文件不存在" flag = False file_dic = {"msg":msg,"flag":flag} send_data_to_client(client, file_dic) else: msg = "文件存在" flag = True file_size = os.path.getsize(file_path) file_dic = {"msg":msg,"flag":flag,"filename":filename,"file_size":file_size} send_data_to_client(client,file_dic) with open(file_path,"rb") as f: for line in f: client.send(line) #总成函数 def task(client): while True: try: data = get_recv_data(client) print(data) if not data: client.close() return #功能列表 funs = {"1":regist,"2":login,"3":upload} print(1) func_id = data["func_id"] print(2) funs[str(func_id)](client,data) print(3) except Exception: print("客户端断开连接") client.close() break pool = ThreadPoolExecutor() while True: client,addr = server.accept() pool.submit(task,client)