一、获取握手信息
首先通过如下代码,我们使用socket来获取客户端的握手信息
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 8002))
sock.listen(5)
conn, address = sock.accept() # 获取客户端的socket对象和地址
msg = conn.recv(1024) # 获取客户端的握手信息
print(msg)
我们可以通过http://www.websocket-test.com/来作为websocket客户端
当连接上服务端时,服务端打印如下信息
二、格式化websocket请求头
将上述打印信息转为JSON格式:
def get_headers(data):
"""
将请求头格式化成字典
:param data:
:return:
"""
header_dict = {}
data = str(data, encoding='utf-8')
# for i in data.split('\r\n'):
# print(i)
header, body = data.split('\r\n\r\n', 1)
header_list = header.split('\r\n')
for i in range(0, len(header_list)):
if i == 0:
if len(header_list[i].split(' ')) == 3:
header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
else:
k, v = header_list[i].split(':', 1)
header_dict[k] = v.strip()
return header_dict
调用上述方法可以将服务端收到的握手信息转为Json格式:
三、提取key并加密
key = headers_dict["Sec-WebSocket-Key"] # 提取key
# 对key进行加密
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = key + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
# print(ac)
四、将加密的结果返回客户端
# 将加密的结果返回客户端
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
"WebSocket-Location: ws://%s%s\r\n\r\n"
response_str = response_tpl % (ac.decode('utf-8'), headers_dict['Host'], headers_dict['url'])
conn.send(response_str.encode("utf-8"))
五、和客户端建立连接后的操作
这里建立连接后,将客户端发送的信息再返回给客户端
while True:
try:
info = conn.recv(8096)
except Exception as e:
info = None
if not info:
break
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')
send_msg(conn, body.encode('utf-8'))
服务端完整代码
实现了服务端接收与发送的分离;
当服务端接收到指定字符串时("exit"),服务端主动断开连接。
import socket,base64,hashlib,time,threading
def get_headers(data):
"""
将请求头格式化成字典
:param data:
:return:
"""
header_dict = {}
data = str(data, encoding='utf-8')
# for i in data.split('\r\n'):
# print(i)
header, body = data.split('\r\n\r\n', 1)
header_list = header.split('\r\n')
for i in range(0, len(header_list)):
if i == 0:
if len(header_list[i].split(' ')) == 3:
header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
else:
k, v = header_list[i].split(':', 1)
header_dict[k] = v.strip()
return header_dict
def send_msg(conn, msg_bytes):
"""
WebSocket服务端向客户端发送消息
:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
:param msg_bytes: 向客户端发送的字节
:return:
"""
import struct
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
conn.send(msg)
return True
def recv_msg(conn):
while True:
try:
info = conn.recv(8096)
except Exception as e:
info = None
if not info:
break
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)
global body
body = str(bytes_list, encoding='utf-8')
print(body)
if body == "exit":
conn.close()
break
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 8002))
sock.listen(5)
conn, address = sock.accept() # 获取客户端的socket对象和地址
data = conn.recv(1024) # 获取客户端的握手信息
headers_dict = get_headers(data)
key = headers_dict["Sec-WebSocket-Key"] # 提取key
# 对key进行加密
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = key + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
# print(ac)
# 将加密的结果返回客户端
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
"WebSocket-Location: ws://%s%s\r\n\r\n"
response_str = response_tpl % (ac.decode('utf-8'), headers_dict['Host'], headers_dict['url'])
conn.send(response_str.encode("utf-8"))
# 和客户端建立连接后的操作
# 启动接收客户端信息的线程
thread_obj = threading.Thread(target=recv_msg, args=(conn,))
thread_obj.daemon = True
thread_obj.start()
# 不断给客户端发送信息
while True:
if getattr(conn, "_closed") == False: # 客户端没有关闭
send_msg(conn, str("你好").encode('utf-8'))
time.sleep(1)
else: # 客户端关闭
break
参考视频链接:https://www.bilibili.com/video/BV1tf4y1K7Ww?p=8&spm_id_from=pageDriver&vd_source=36a3e35639c44bb339f59760641390a8
参考文章:https://www.cnblogs.com/wupeiqi/p/6558766.html