websocket原理html
首先,Websocket是一個持久化的協議,相對於HTTP這種非持久的協議來講。web
HTTP的生命週期經過 Request
來界定,也就是一個 Request
一個 Response
,那麼在 HTTP1.0
中,此次HTTP請求就結束了。瀏覽器
在HTTP1.1中進行了改進,使得有一個keep-alive,也就是說,在一個HTTP鏈接中,能夠發送多個Request,接收多個Response。可是請記住 Request = Response
, 在HTTP中永遠是這樣,也就是說一個request只能有一個response。並且這個response也是被動的,不能主動發起。服務器
Websocket是基於HTTP協議的,或者說借用了HTTP的協議來完成一部分握手。websocket
首先咱們來看個典型的 Websocket
握手app
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com
熟悉HTTP的童鞋可能發現了,這段相似HTTP協議的握手請求中,多了幾個東西。我會順便講解下做用。socket
Upgrade: websocket Connection: Upgrade
這個就是Websocket的核心了,告訴 Apache
、 Nginx
等服務器:注意啦,我發起的是Websocket協議,快點幫我找到對應的助理處理~不是那個老土的HTTP。ide
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13
Sec-WebSocket-Key
是一個 Base64 encode
的值,這個是瀏覽器隨機生成的,告訴服務器:泥煤,不要忽悠窩,我要驗證尼是否是真的是Websocket助理。加密
而後, Sec_WebSocket-Protocol
是一個用戶定義的字符串,用來區分同URL下,不一樣的服務所須要的協議。spa
最後, Sec-WebSocket-Version
是告訴服務器所使用的 Websocket Draft
(協議版本),在最初的時候,Websocket協議還在 Draft
階段,各類奇奇怪怪的協議都有,並且還有不少期奇奇怪怪不一樣的東西,什麼Firefox和Chrome用的不是一個版本之類的,後來作了統一。
而後服務器會返回下列東西,表示已經接受到請求, 成功創建Websocket啦!
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
這裏開始就是HTTP最後負責的區域了,告訴客戶,我已經成功切換協議啦~
Upgrade: websocket Connection: Upgrade
依然是固定的,告訴客戶端即將升級的是 Websocket
協議,而不是mozillasocket,lurnarsocket或者shitsocket。
而後, Sec-WebSocket-Accept
這個則是通過服務器確認,而且加密事後的 Sec-WebSocket-Key
。
後面的, Sec-WebSocket-Protocol
則是表示最終使用的協議。
至此,HTTP已經完成它全部工做了。
實例
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>lujiacheng</h1> <script> ws=new WebSocket("ws://127.0.0.1:8096") ws.onopen=function () { alert('successful'); ws.send("Hellowww"); } ws.onmessage=function (event) { alert(event.data) } </script> </body> </html>
import socket import base64 import hashlib import struct # 向客戶端發送消息 def send_msg(conn,msg_bytes): token=b"\x81" length=len(msg_bytes) if length<126: token+=struct.pack("B",length) elif length<0xFFFF: token+=struct.pack("!BH",126,length) else: token+=struct.pack("!BQ",127,length) msg=token+msg_bytes print(msg) conn.send(msg) return True def get_websocket_message(str_header): header,body=str_header.split(b'\r\n\r\n') header_dict={} headers=header.split(b'\r\n') for str_spilt in headers: str_hd=str_spilt.split(b':') if len(str_hd)==2: header_dict[str(str_hd[0],encoding='utf-8')]=str(str_hd[1],encoding='utf-8').strip() magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' values=header_dict['Sec-WebSocket-Key']+magic_string ac=base64.b64encode(hashlib.sha1(values.encode('utf-8')).digest()) request_tpl="HTTP/1.1 101 Switching Protocols\r\n" \ "Upgrade: websocket\r\n" \ "Connection: Upgrade\r\n" \ "Sec-WebSocket-Accept: %s\r\n\r\n" %(str(ac,encoding='utf-8')) return request_tpl.encode('utf-8') sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sock.bind(('127.0.0.1',8096)) sock.listen(5) # 等待用戶鏈接 conn,addr=sock.accept() # 握手消息 data=conn.recv(8096) # 獲取握手消息,magic_string,shal加密 # 發送給客戶端 recv=get_websocket_message(data) conn.send(recv) # 創建成功後,接收客戶端發過來的消息 info = conn.recv(1024) # 解析客戶端發過來的數據 while True: # payload_len決定數據頭和數據所佔的位數 # payload_len等於126,要在日後移2字節,即16bit # payload_len等於127,要在日後移8字節,即64bit # mask解碼 payload_len=info[1] & 127 if payload_len==126: extend_payload_len=info[2:4] mask=info[4:8] decoded=info[8:] elif payload_len==127: extend_payload_len = info[2:10] mask = info[10:14] decoded = info[14:] else: extend_payload_len = None mask = info[2:6] decoded = info[6:] bytes_list=bytearray() for i in range(len(decoded)): chunk=decoded[i] ^ mask[i % 4] bytes_list.append(chunk) body=str(bytes_list,encoding="utf-8") print(body) text=input(">>>") send_msg(conn,bytes(text,encoding="utf-8"))