websocket工作原理
websocket是什么?
websocket是一套类似于http的协议。
扩展:
http协议:\r\n分割、请求头和请求体\r\n分割、无状态、短连接。
{'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_SOFTWARE': 'gevent/1.4 Python/3.6', 'SCRIPT_NAME': '', 'wsgi.version': (1, 0), 'wsgi.multithread': False, 'wsgi.multiprocess': False, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.errors': <_io.TextIOWrapper name='' mode='w' encoding= 'UTF-8'>, 'SERVER_NAME': 'PC-20180312LANS', 'SERVER_PORT': '9527', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/conn_ws', 'QUERY_STRING': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REMOTE_ADDR': '192.168.xx.xxx', 'REMOTE_PORT': '53449', 'HTTP_HOST': '192.168.xx.xxx:9527', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121Safari/537.36', 'HTTP_ACCEPT': 'text/html, application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_ACCEPT_LANGUAGE': 'zh,en-US;q=0.9,en;q=0.8,zh-CN;q=0.7', 'wsgi.input': , 'wsgi.input_terminated': True, 'werkzeug.request': http://192.168.11.133:9527/conn_ws' [GET]>}
websocket协议:\r\n分割,创建连接后不断开、验证+数据加密;
{'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_SOFTWARE': 'gevent/1.4 Python/3.6', 'SCRIPT_NAME': '', 'wsgi.version': (1, 0), 'wsgi.multithread': False, 'wsgi.multiprocess': False, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.errors': <_io.TextIOWrapper name='' mode='w' encoding= 'UTF-8'>, 'SERVER_NAME': 'PC-20180312LANS', 'SERVER_PORT': '9527', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/conn_ws', 'QUERY_STRING': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '53571', 'HTTP_HOST': '127.0.0.1:9527', 'HTTP_CONNECTION': 'Upgrade', 'HTTP_PRAGMA': 'no-cache', 'HTTP_CACHE_CONTROL': 'no-cache', 'HTTP_UPGRADE': 'websocket', 'HTTP_ORIGIN': 'http://localhost:63342', 'HTTP_SEC_WEBSOCKET_VERSION': '13', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh,en-US;q=0.9,en;q=0.8,zh-CN;q=0.7', 'HTTP_SEC_WEBSOCKET_KEY': 'ET/SDQc1sI+uhxm+EjHLcw==', 'HTTP_SEC_WEBSOCKET_EXTENSIONS': 'permessage-deflate; client_max_window_bits', 'wsgi.input': object at 0x0000000003BC9468>, 'wsgi.input_terminated': True, 'wsgi.websocket_version': '13',
其中主要不同的参数为:
'wsgi.websocket':object at 0x0000000003BC8528>, 'werkzeug.request': 'http://127.0.0.1:9527/conn_ws' [GET]>}
websocket本质:
就是一个创建连接后不断开的socket,当连接成功之后:
客户端(浏览器)会自动向服务端发送消息,包含: Sec-WebSocket-Key : jocLOLLq1BQWp0aZgEWL5A==
通过header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip()返回一个header_dict字典
服务端接收之后,会对于该数据进行加密:
value = headers['Sec-WebSocket-Key'] + magic_string ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest()) 其中: magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11
构造响应头:
1 response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \ 2 "Upgrade:websocket\r\n" \ 3 "Connection: Upgrade\r\n" \ 4 "Sec-WebSocket-Accept: %s\r\n" \ 5 "WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n"
发送客户端(浏览器)
-建立:双工通道,接下来就可以进行收发数据
-发送的数据是经过加密数据,
-解密根据payload_len的值进行处理:
-payload_len <= 125
-payload_len == 126
-payload_len == 127
获取内容:
-mask_key
数据
根据mask_key和数据进行位运算,就可以把值解析出来。
1 hashstr = b'\x81\x83\xceH\xb6\x85\xffz\x85' 2 # b'\x81 \x83 \xceH\xb6\x85\xffz\x85' 3 # 将第二个字节也就是 \x83 第9-16位 进行与127进行位运算 4 # 127二进制为01111111 进行"与"位运算 结果是两个数的最小值 5 payload = hashstr[1] & 127 6 print(payload) 7 if payload == 127: 8 extend_payload_len = hashstr[2:10] 9 mask = hashstr[10:14] 10 decoded = hashstr[14:] 11 # 当位运算结果等于127时,则第3-10个字节为数据长度 12 # 第11-14字节为mask 解密所需字符串 13 # 则数据为第15字节至结尾 14 15 if payload == 126: 16 extend_payload_len = hashstr[2:4] 17 mask = hashstr[4:8] 18 decoded = hashstr[8:] 19 # 当位运算结果等于126时,则第3-4个字节为数据长度 20 # 第5-8字节为mask 解密所需字符串 21 # 则数据为第9字节至结尾 22 23 if payload <= 125: 24 extend_payload_len = None 25 mask = hashstr[2:6] 26 decoded = hashstr[6:] 27 28 # 当位运算结果小于等于125时,则这个数字就是数据的长度 29 # 第3-6字节为mask 解密所需字符串 30 # 则数据为第7字节至结尾 31 32 str_byte = bytearray() 33 34 for i in range(len(decoded)): 35 byte = decoded[i] ^ mask[i % 4] 36 str_byte.append(byte) 37 38 print(str_byte.decode("utf8"))
1 import struct 2 msg_bytes = "hello".encode("utf8") 3 token = b"\x81" 4 length = len(msg_bytes) 5 6 if length < 126: 7 token += struct.pack("B", length) 8 elif length == 126: 9 token += struct.pack("!BH", 126, length) 10 else: 11 token += struct.pack("!BQ", 127, length) 12 13 msg = token + msg_bytes 14 15 print(msg)