验证客户端的合法性
一对多聊天,不用ip,port来判断和谁聊天
1 import socket 2 friend_lst = {'alex':'32','太白':'33'} 3 sk =socket.socket(type=socket.SOCK_DGRAM) 4 sk.bind(('127.0.0.1',9001)) 5 while True: 6 msg,addr = sk.recvfrom(1500) 7 msg = msg.decode('utf-8') 8 name,message = msg.split('|',1) # 只切一次 9 print('\033[1;%sm %s:%s\033[0m'%(friend_lst.get(name,'30'),name,message)) 10 content = input('>>>') 11 sk.sendto(content.encode('utf-8'),addr)
client端
1 import socket 2 name = '大壮' 3 sk = socket.socket(type=socket.SOCK_DGRAM) 4 5 while True: 6 content = input('>>>') 7 if content.upper() == 'Q': break 8 content = '%s|%s'%(name,content) 9 sk.sendto(content.encode('utf-8'),('127.0.0.1',9001)) 10 msg = sk.recv(1024).decode('utf-8') 11 if msg.upper() == 'Q': break 12 print(msg)
传送小文件
server端
1 import json 2 import socket 3 # 接收 4 sk = socket.socket() 5 sk.bind(('127.0.0.1',9001)) 6 sk.listen() 7 8 conn,_ = sk.accept() 9 msg = conn.recv(1024).decode('utf-8') 10 msg = json.loads(msg) 11 12 with open(msg['filename'],'wb') as f: 13 content = conn.recv(msg['filesize']) 14 print('-->',len(content)) 15 f.write(content) 16 conn.close() 17 sk.close()
client端
1 import os 2 import json 3 import socket 4 # 发送 5 sk = socket.socket() 6 sk.connect(('127.0.0.1',9001)) 7 8 # 文件名\文件大小 9 abs_path = r'D:\python_22\day30\tmp' 10 filename = os.path.basename(abs_path) 11 filesize = os.path.getsize(abs_path) 12 dic = {'filename':filename,'filesize':filesize} 13 str_dic = json.dumps(dic) 14 sk.send(str_dic.encode('utf-8')) 15 16 with open(abs_path,mode = 'rb') as f: 17 content = f.read() 18 sk.send(content) 19 20 sk.close()
总结:把文件名和文件大小先做一个字典用json传过去,打开一个同名文件,只接受文件大小的数据
传送大文件
server端
1 import json 2 import struct 3 import socket 4 # 接收 5 sk = socket.socket() 6 sk.bind(('127.0.0.1',9001)) 7 sk.listen() 8 9 conn,_ =sk.accept() 10 msg_len = conn.recv(4) 11 dic_len = struct.unpack('i',msg_len)[0] 12 msg = conn.recv(dic_len).decode('utf-8') 13 msg = json.loads(msg) 14 15 with open(msg['filename'],'wb') as f: 16 while msg['filesize'] > 0: 17 content = conn.recv(1024) 18 msg['filesize'] -= len(content) 19 f.write(content) 20 conn.close() 21 sk.close()
client端
1 import os 2 import json 3 import struct 4 import socket 5 # 发送 6 sk = socket.socket() 7 sk.connect(('127.0.0.1',9001)) 8 9 # 文件名\文件大小 10 abs_path = r'D:\python22期\day28 课上视频\3.网络基础概念.mp4' 11 filename = os.path.basename(abs_path) 12 filesize = os.path.getsize(abs_path) 13 dic = {'filename':filename,'filesize':filesize} 14 str_dic = json.dumps(dic) 15 b_dic = str_dic.encode('utf-8') 16 mlen = struct.pack('i',len(b_dic)) 17 sk.send(mlen) # 4个字节 表示字典转成字节之后的长度 18 sk.send(b_dic) # 具体的字典数据 19 20 with open(abs_path,mode = 'rb') as f: 21 while filesize>0: 22 content = f.read(1024) 23 filesize -= len(content) 24 sk.send(content) 25 sk.close()
总结:传送大文件时,不能保证每次接收的都是限定的字节,因为tcp优化会把大文件拆成小文件传送,所以实际接收的文件会比上限要小,应该总文件大小 -= 传过来的内容大小。粘包发生在传送字典时,为了防止粘包所以先传字典长度,因为是一个大文件所以粘包无所谓。
今日内容
验证客户端的合法性
用处
当需要考虑是否有恶意客户端访问会影响服务器正常运行时。比如恶意客户端会通过扫端口等方法盗取信息或传送非法文件。
认证机制
服务端获取一个随机字符串,发送给客户端,之后服务端会把秘钥与发送的内容经过算法得出一个结果,此时客户端也会把秘钥与接收的内容经过同样的算法,将得出的结果发送给服务端,服务端通过比较两者结果是否相同判定客户端是否合法。
秘钥:用于验证身份的一段约定好的数据
随机字符串:发送的内容可能会被截获,为了安全一次认证后需换一个随机内容
算法:hashlib模块
1 # import os 2 # ret = os.urandom(16) 3 # print(ret) 4 5 # import hashlib 6 # sha = hashlib.sha1(密钥) 7 # sha.update(随机字符串) 8 # 结果 = sha.hexdigest()
hmac模块
1 # h = hmac.new(b'alex_sb',os.urandom(32)) 2 # ret = h.digest() 3 # print(ret)
实现代码
server端
1 import os 2 import socket 3 import hashlib 4 5 secret_key = b'alex_sb' 6 sk = socket.socket() 7 sk.bind(('127.0.0.1',9001)) 8 sk.listen() 9 10 conn,addr = sk.accept() 11 # 创建一个随机的字符串 12 rand = os.urandom(32) 13 # 发送随机字符串 14 conn.send(rand) 15 16 # 根据发送的字符串 + secrete key 进行摘要 17 sha = hashlib.sha1(secret_key) 18 sha.update(rand) 19 res = sha.hexdigest() 20 21 # 等待接收客户端的摘要结果 22 res_client = conn.recv(1024).decode('utf-8') 23 # 做比对 24 if res_client == res: 25 print('是合法的客户端') 26 # 如果一致,就显示是合法的客户端 27 # 并可以继续操作 28 conn.send(b'hello') 29 else: 30 conn.close() 31 # 如果不一致,应立即关闭连接
client端
1 import socket 2 import hashlib 3 4 secret_key = b'alex_sb979' 5 sk = socket.socket() 6 sk.connect(('127.0.0.1',9001)) 7 8 # 接收客户端发送的随机字符串 9 rand = sk.recv(32) 10 # 根据发送的字符串 + secret key 进行摘要 11 sha = hashlib.sha1(secret_key) 12 sha.update(rand) 13 res = sha.hexdigest() 14 # 摘要结果发送回server端 15 sk.send(res.encode('utf-8')) 16 # 继续和server端进行通信 17 msg = sk.recv(1024) 18 print(msg)
并发的tcp协议server端 — socketserver
socketserver是socket的底层模块
底层模块
底层模块的效率全看使用者的使用方法
实现代码
server端(背)
1 import time 2 import socketserver 3 4 class Myserver(socketserver.BaseRequestHandler): 5 def handle(self): 6 conn = self.request 7 while True: 8 try: 9 content = conn.recv(1024).decode('utf-8') 10 conn.send(content.upper().encode('utf-8')) 11 time.sleep(0.5) 12 except ConnectionResetError: 13 break 14 server = socketserver.ThreadingTCPServer(('127.0.0.1',9001),Myserver) 15 server.serve_forever()
client端(没改动)
1 import socket 2 3 sk = socket.socket() 4 sk.connect(('127.0.0.1',9001)) 5 6 while True: 7 sk.send(b'hello') 8 content = sk.recv(1024).decode('utf-8') 9 print(content)