1.客戶端向服務端發送握手包web
GET ws://localhost:8000/ HTTP/1.1
Host: localhost:8000
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:63342
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8
Sec-WebSocket-Key: 60jj9YDlrvPB+DzLmVJzcg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits複製代碼
2.服務端進行迴應,返回給客戶端瀏覽器
HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: Cx7gYudkmGmF8rMFP1W1HbLkWRA=複製代碼
客戶端發送握手請求,而後服務端返回正確的數據,通過客戶端的確認完成握手,而後就能夠進行通訊了;
這裏客戶端發送的Sec-WebSocket-Key
是一個Base64 encode的值,它是瀏覽器隨機生成的,而後服務端經過這個值生成一個Sec-WebSocket-Accept
;
方法: key+'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
,而後SHA-1加密,再進行base-64加密;
下面是握手代碼:緩存
HANDSHAKE_STR = (
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %(token)s\r\n\r\n"
)
GUID_STR = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
def parseHeaders(self, msg):
# print(msg)
headers = {}
msg = str(msg, encoding="utf-8")
header, data = msg.split('\r\n\r\n', 1)
for x in header.split("\r\n")[1:]:
# 這裏是 「:+space」
key, value = x.split(": ", 1)
headers[key] = value
return headers
# 握手數據
def handShakeData(self,c_req):
sec_key = self.parseHeaders(c_req)['Sec-WebSocket-Key']
key = sec_key.encode("ascii") + GUID_STR.encode("ascii")
accept_token = base64.b64encode(hashlib.sha1(key).digest()).decode("ascii")
s_res = HANDSHAKE_STR % {'token': accept_token}
return s_res複製代碼
大概就是握手完成後,客戶端發送消息到服務端,而後服務端將消息發送到每一個鏈接上的客戶端;bash
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+複製代碼
0x0
表示爲附加數據0x1
表示爲text類型數據0x2
表示binary類型數據0x3~7
暫未定義0x8
表示鏈接關閉0x9
表示ping類型數據0xA
表示pong類型數據0xB~F
暫未定義方法:將Payload Data數據的每一位x,與Masking-key的第i%4位進行異或運算,i是x在Payload Data中的索引;
下面是解析的代碼:websocket
def parseData(self,msg):
g_code_length = msg[1] & 127
if g_code_length == 126:
g_code_length = struct.unpack('!H', msg[2:4])[0]
masks = msg[4:8]
data = msg[8:]
elif g_code_length == 127:
g_code_length = struct.unpack('!Q', msg[2:10])[0]
masks = msg[10:14]
data = msg[14:]
else:
masks = msg[2:6]
data = msg[6:]
i = 0
raw_by = bytearray()
for d in data:
raw_by.append( int(d) ^ int(masks[i % 4]) )
i += 1
print(u"總長度是:%d" % int(g_code_length))
raw_str = raw_by.decode()
return raw_str複製代碼
發送的數據格式:固定字節+包長度字節+原始數據
例:\x81\x0cHello World!
app
三. 完整的代碼
服務端:socket
import socket
import base64,hashlib
import threading
import struct
from collections import deque
HANDSHAKE_STR = (
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %(token)s\r\n\r\n"
)
GUID_STR = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
STREAM = 0x0
TEXT = 0x1
BINARY = 0x2
CLOSE = 0x8
PING = 0x9
PONG = 0xA
class WebSocket(threading.Thread):
def __init__(self,conn,addr,index):
threading.Thread.__init__(self)
self.index = index
self.conn = conn
self.addr = addr
#發送的數據緩存
self.buffer = bytearray()
self.sendToClientData = deque()
# 組裝header 獲取‘Sec-WebSocket-Key’
def parseHeaders(self, msg):
# print(msg)
headers = {}
msg = str(msg, encoding="utf-8")
header, data = msg.split('\r\n\r\n', 1)
for x in header.split("\r\n")[1:]:
# 這裏是 「:+space」
key, value = x.split(": ", 1)
headers[key] = value
return headers
# 握手數據
def handShakeData(self,c_req):
sec_key = self.parseHeaders(c_req)['Sec-WebSocket-Key']
key = sec_key.encode("ascii") + GUID_STR.encode("ascii")
accept_token = base64.b64encode(hashlib.sha1(key).digest()).decode("ascii")
s_res = HANDSHAKE_STR % {'token': accept_token}
return s_res
# 獲得數據長度 (包含描述字節)
def getMsglen(self,msg):
g_code_length = msg[1] & 127
if g_code_length == 126:
g_code_length = struct.unpack('!H', msg[2:4])[0]
g_code_length += 8
elif g_code_length == 127:
g_code_length = struct.unpack('!Q', msg[2:10])[0]
g_code_length += 14
else:
g_code_length += 6
g_code_length = int(g_code_length)
print(g_code_length)
return g_code_length
# 解析數據
def parseData(self,msg):
g_code_length = msg[1] & 127
if g_code_length == 126:
g_code_length = struct.unpack('!H', msg[2:4])[0]
masks = msg[4:8]
data = msg[8:]
elif g_code_length == 127:
g_code_length = struct.unpack('!Q', msg[2:10])[0]
masks = msg[10:14]
data = msg[14:]
else:
masks = msg[2:6]
data = msg[6:]
i = 0
raw_by = bytearray()
for d in data:
raw_by.append( int(d) ^ int(masks[i % 4]) )
i += 1
print(raw_by)
print(u"總長度是:%d" % int(g_code_length))
raw_str = raw_by.decode()
# raw_str = str(raw_by)
return raw_str
# 發送消息
def sendMessage(self,message):
# 遍歷鏈接上的列表
for conn in connections.values():
# 發送消息到client (除了本身)
if conn != self.conn:
self._sendMessage(False,TEXT,message)
while self.sendToClientData:
data = self.sendToClientData.popleft()
conn.send(data[1])
# FIN 後面是否還有數據;opcode 傳輸的數據包類型;message 傳輸的數據(str)
def _sendMessage(self,FIN,opcode,message):
payload = bytearray()
b1 = 0
b2 = 0
if FIN is False:
b1 |= 0x80
b1 |= opcode # 若opcode=TEXT b'0x81'
payload.append(b1) #
msg_utf = message.encode('utf-8')
msg_len = len(msg_utf)
if msg_len <= 125:
b2 |= msg_len
payload.append(b2)
elif msg_len >= 126 and msg_len <= 65535:
b2 |= 126
payload.append(b2)
payload.extend(struct.pack("!H", msg_len))
elif msg_len <= (2 ^ 64 - 1):
b2 |= 127
payload.append(b2)
payload.extend(struct.pack("!Q", msg_len))
else:
print("傳輸的數據太長了! ——(_sendMessage)")
# 拼接上須要發送的數據
# 格式大概這樣:bytearray(b'\x81\x0cHello World!') '\x0c'是發送的數據長度
if msg_len > 0:
payload.extend(msg_utf)
self.sendToClientData.append((opcode,payload))
# 端開鏈接
def _disConnected(self):
self.conn.close()
print("斷開的鏈接conn%s" % (self.index))
print(connections)
del connections['conn%s' % (self.index)]
print(connections)
#
def run(self):
# print(self.listeners)
self.isHandShake = False
while True:
if self.isHandShake == False:
print("start handshake %s" % (self.addr[0]))
# 接收客戶端內容
c_req = self.conn.recv(1024)
# print(c_req)
handData = self.handShakeData(c_req)
self.conn.sendall(str.encode(handData))
self.isHandShake = True
else:
message = self.conn.recv(16384) # 16 * 1024 16384 # bytes
print(message)
# 斷開鏈接
if message[0]&127 == CLOSE: # 0x88 136
self._disConnected()
return
self.buffer.extend(message) # bytes
code_length = self.getMsglen(self.buffer) # 數據中帶的 數據長度(包含描述字節)
# 數據完整就發送
if code_length == len(self.buffer):
buffer_utf = self.parseData(self.buffer) # str
self.sendMessage(buffer_utf)
print(message)
self.buffer = bytearray()
connections = {}
class websocketServer(object):
def __init__(self,host, port):
self.serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.serversocket.bind((host, port))
self.serversocket.listen(5)
def server(self):
print(self.serversocket)
index =0
while True:
conn,addr = self.serversocket.accept()
print(conn)
print(addr)
# 新建線程
newSocket = WebSocket(conn,addr,index)
# 開始線程,執行run函數
newSocket.start()
connections['conn%s'% (index)] = conn
index += 1
print(connections)
websocketServer = websocketServer("127.0.0.1",8000)
websocketServer.server()複製代碼
感謝您的閱讀;函數