参考:https://www.cnblogs.com/wangqj1996/p/9244601.html
一、什么是websocket
WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
二、客户端请求报文
客户端请求的链接:ws://localhost:8080
和传统http报文不同的地方:
Connection: Upgrade
Upgrade: websocket ----- 表示发起的是websocket协议
Sec-WebSocket-Key: TD7emWUct4iW4vddYWbMqQ== ------ 由浏览器随机生成,提供基本的防护
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits ---- 协议的扩展
Sec-WebSocket-Version: 13 ---- 版本号
三、服务器接收请求报文
服务器收到请求报文后,会发起tcp的三次握手,和客户端建立链接,这个地方和tcpsocket基本一样。
# 创建基于tcp的服务器
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
host = (HOST, PORT)
serverSocket.bind(host)
serverSocket.listen(128)
print("服务器运行, 等待用户链接")
while True:
# print("getting connection")
clientSocket, addressInfo = serverSocket.accept()
# print("get connected")
request = clientSocket.recv(2048)
print(request.decode())
# 获取Sec-WebSocket-Key
ret = re.search(r"Sec-WebSocket-Key: (.*==)", str(request.decode()))
if ret:
key = ret.group(1)
else:
return
Sec_WebSocket_Key = key + MAGIC_STRING
# print("key ", Sec_WebSocket_Key)
# 将Sec-WebSocket-Key先进行sha1加密,转成二进制后在使用base64加密
response_key = base64.b64encode(hashlib.sha1(bytes(Sec_WebSocket_Key, encoding="utf8")).digest())
response_key_str = str(response_key)
response_key_str = response_key_str[2:30]
# print(response_key_str)
# 构建websocket返回数据
response = HANDSHAKE_STRING.replace("{1}", response_key_str).replace("{2}", HOST + ":" + str(PORT))
clientSocket.send(response.encode())
# print("send the hand shake data")
四、因为websocket是基于tcp的全双工通信协议,所以,他可以一边接收,一边发送
1、接收并解析websocket报文
b'\x81\x84\xa3l\xcf\x10\x92^\xfc$'
客户端发送到server的websocket的报文分为四个部分:
a、固定部分‘\81’
b、报文内容长度
c、掩码 b'\xa3l\xcf\x10'
d、报文内容 b'\x92^\xfc$'
def recv_data(clientSocket):
try:
info = clientSocket.recv(2048)
if not info:
return
except:
return
else:
code_len = info[1] & 0x7f
if code_len == 0x7e:
extend_payload_len = info[2:4]
mask = info[4:8]
decoded = info[8:]
elif code_len == 0x7f:
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)
raw_str = str(bytes_list, encoding="utf-8")
print(raw_str)
网上还有另一种方法,待测试:
握手成功后前端向服务器发送报文信息,信息内容有中文。解析过程中如何解决乱码问题?
msg = decode(self.conn.recv(1024)) #接收报文
#解析报文
def decode(data):
if not len(data):
return False
length = data[1] & 127
if length == 126:
mask = data[4:8]
raw = data[8:]
elif length == 127:
mask = data[10:14]
raw = data[14:]
else:
mask = data[2:6]
raw = data[6:]
ret = ''
for cnt, d in enumerate(raw):
ret += chr(d ^ mask[cnt % 4])
return ret
、 2、server端发送数据
server端发送数据分为三个部分
a、固定部分‘\81’
b、报文长度
c、报文内容
struct用法:
Format C Type Python type Standard size Notes
x pad byte no value
c char bytes of length 1 1
b signed char integer 1 (1),(3)
B unsigned char integer 1 (3)
? _Bool bool 1 (1)
h short integer 2 (3)
H unsigned short integer 2 (3)
i int integer 4 (3)
I unsigned int integer 4 (3)
l long integer 4 (3)
L unsigned long integer 4 (3)
q long long integer 8 (2), (3)
Q unsigned long long integer 8 (2), (3)
n ssize_t integer (4)
N size_t integer (4)
e (7) float 2 (5)
f float float 4 (5)
d double float 8 (5)
s char[] bytes
p char[] bytes
P void * integer (6)
Character Byte order Size Alignment
@ native native native
= native standard none
< little-endian standard none
> big-endian standard none
! network (= big-endian) standard none
服务端发送数据代码:
def send_data(clientSocket):
data = "need to send messages中文"
token = b'\x81'
length = len(data.encode())
if length<=125:
token += struct.pack('B', length)
elif length <= 0xFFFF:
token += struct.pack('!BH', 126, length)
else:
token += struct.pack('!BQ', 127, length)
data = token + data.encode()
clientSocket.send(data)
全部代码:
scoket_demo.py:
import socket
import base64
import hashlib
import re
import threading
import struct
HOST = "localhost"
PORT = 8080
MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: {1}\r\n" \
"WebSocket-Location: ws://{2}/chat\r\n" \
"WebSocket-Protocol:chat\r\n\r\n"
def recv_data(clientSocket):
try:
info = clientSocket.recv(2048)
if not info:
return
except:
return
else:
print(info)
code_len = info[1] & 0x7f
if code_len == 0x7e:
extend_payload_len = info[2:4]
mask = info[4:8]
decoded = info[8:]
elif code_len == 0x7f:
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()
print(mask)
print(decoded)
for i in range(len(decoded)):
chunk = decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
raw_str = str(bytes_list, encoding="utf-8")
print(raw_str)
def send_data(clientSocket):
data = "need to send messages中文"
token = b'\x81'
length = len(data.encode())
if length<=125:
token += struct.pack('B', length)
elif length <= 0xFFFF:
token += struct.pack('!BH', 126, length)
else:
token += struct.pack('!BQ', 127, length)
data = token + data.encode()
clientSocket.send(data)
def handshake(serverSocket):
while True:
# print("getting connection")
clientSocket, addressInfo = serverSocket.accept()
# print("get connected")
request = clientSocket.recv(2048)
print(request.decode())
# 获取Sec-WebSocket-Key
ret = re.search(r"Sec-WebSocket-Key: (.*==)", str(request.decode()))
if ret:
key = ret.group(1)
else:
return
Sec_WebSocket_Key = key + MAGIC_STRING
# print("key ", Sec_WebSocket_Key)
# 将Sec-WebSocket-Key先进行sha1加密,转成二进制后在使用base64加密
response_key = base64.b64encode(hashlib.sha1(bytes(Sec_WebSocket_Key, encoding="utf8")).digest())
response_key_str = str(response_key)
response_key_str = response_key_str[2:30]
# print(response_key_str)
# 构建websocket返回数据
response = HANDSHAKE_STRING.replace("{1}", response_key_str).replace("{2}", HOST + ":" + str(PORT))
clientSocket.send(response.encode())
# print("send the hand shake data")
t1 = threading.Thread(target = recv_data, args = (clientSocket,))
t1.start()
t2 = threading.Thread(target = send_data, args = (clientSocket,))
t2.start()
def main():
# 创建基于tcp的服务器
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
host = (HOST, PORT)
serverSocket.bind(host)
serverSocket.listen(128)
print("服务器运行, 等待用户链接")
# 调用监听
handshake(serverSocket)
if __name__ == "__main__":
main()
html:
w