自己写一个websocket
原理更多参考-- https://www.cnblogs.com/zunzunQ/p/8549180.html
原理简介: client端 握手请求中 包含一个base64 encode的随机字符串, server端 magic_string+str sha1摘要之后base64加密, 发给client 服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦 (回调机制)
import socket, hashlib, base64 sock = socket.socket() sock.bind(('127.0.0.1', 9000)) sock.listen(5) ''' b'GET / HTTP/1.1 Host: 127.0.0.1:9000 Connection: Upgrade Pragma: no-cache Cache-Control: no-cache User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36 Upgrade: websocket --建立-- Origin: http://localhost:63342 Sec-WebSocket-Version: 13 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Sec-WebSocket-Key: vNtQ8bVSNRPL5p+xo1X5CA== 验证 一按钥匙 Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits ''' ''' b'GET / HTTP/1.1 -- 单纯http地址栏访问 Host: 127.0.0.1:9000 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 ' ''' conn, addr = sock.accept() data = conn.recv(1024) # print(data) def get_header(data): # 获取头部信息 header_dict = {} header_str = data.decode('utf8') for i in header_str.split(' '): if str(i).startswith('Sec-WebSocket-Key'): header_dict['Sec-WebSocket-Key'] = i.split(':')[1].strip() return header_dict headers = get_header(data) magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' # magic_string = 魔法字符串 value = headers['Sec-WebSocket-Key'] + magic_string print(value) #mzrsVOiYogmzB8fYcyMBpw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11 ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest()) # 固定加密算法 print(ac) #b'0Cu1dAA7U6tFsJS4EVkIDLcvKDU=' response_tpl = "HTTP/1.1 101 Switching Protocols " "Upgrade:websocket " "Connection: Upgrade " "Sec-WebSocket-Accept: %s " "WebSocket-Location: ws://127.0.0.1:9527 " response_str = response_tpl % (ac.decode('utf-8')) conn.send(response_str.encode('utf8')) # 响应握手信息 while True: msg = conn.recv(8096) print(msg)
解密过程 分析
hashstr = b'x81x89xbcxb8,xb5Y6xa5Px12x0bxc8x0f:' # b'x81 x83 xceHxb6x85xffzx85' # 将第二个字节也就是 x83 第9-16位 进行与127进行 与位 运算 payload = hashstr[1] & 127 print(payload) if payload == 127: extend_payload_len = hashstr[2:10] # 顾头不顾尾 mask = hashstr[10:14] decoded = hashstr[14:] # 当位运算结果等于127时,则第3-10个字节为数据长度 # 第11-14字节为mask 解密所需字符串 # 则数据为第15字节至结尾 if payload == 126: extend_payload_len = hashstr[2:4] mask = hashstr[4:8] decoded = hashstr[8:] # 当位运算结果等于126时,则第3-4个字节为数据长度 255*255+510=65535 21845汉字 # 第5-8字节为mask 解密所需字符串 # 则数据为第9字节至结尾 if payload <= 125: extend_payload_len = None mask = hashstr[2:6] decoded = hashstr[6:] # 当位运算结果小于等于125时,则这个数字就是数据的长度 # 第3-6字节为mask 解密所需字符串 # 则数据为第7字节至结尾 str_byte = bytearray() # 字节流 for i in range(len(decoded)): byte = decoded[i] ^ mask[i % 4] # 异或运算 上下为0就是0 上下有1就是1 str_byte.append(byte) print(str_byte.decode("utf8")) # 主要解决中文 厉害了 # # 1字节 = 8 bit 与位运算 # 01111111 # 11111111 最大255 # 2/4/8/16/32/64/138/256
加密过程 -- 分析
import struct msg_bytes = "hello".encode("utf8") #转bytes token = b"x81" # 数据标准格式 length = len(msg_bytes) #第二位就是数据长度 if length < 126: token += struct.pack("B", length) elif length == 126: token += struct.pack("!BH", 126, length) else: token += struct.pack("!BQ", 127, length) msg = token + msg_bytes print(msg)
总结
WebSocket的工作原理 1.握手 客户端 Sec-WebSocket-Key 响应 base64(sha1(Sec-WebSocket-Key + magic_string)) 2.解密: 与127 进行"与"位运算 结果是两个数的最小值 1. == 127 第3-10个字节表示该数据的长度 2. == 126 第3-4个字节表示该数据的长度 00000010 00000000 3. <= 125 当前这个数字就是Websocket发送的数据长度