Websocket数据解析全过程
之前在协助建军做⾃动通知前端刷新和最近分⽚上传⼤⽂件的时候都使⽤到了websocket,对websocket做了⼀些调研,这篇⽂章就跟⼤家⼀起分享⼀下。
websocket的使⽤场景:
⼀般我们做聊天室这种需要保持通讯状态的功能,如果是⽤HTTP协议去做的话,需要客户端⼀直做长轮询去查询服务端是否由新消息,再显⽰在客户端上,这是因为HTTP本⾝是⼀个短连接,当我们客户端发起请求时,服务器端响应完请求将结果返回给客户端,这个连接就⾃动断开了,这个过程http服务端是被动的,⽽websocket不仅客户端可以做请求响应,服务器端也可以做请求响应,即它可以实现往客户端主动推送消息,这样做聊天室就⾮常⽅便了。
websocket它实现了浏览器与服务器全双⼯(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进⾏通信。⽽HTTP只⽀持单⼯通信,http⾃动断开,websocket会⼀直保持长连接,除⾮你主动请求断开
WebSocket ⽬前在各⼤主流浏览器的⽀持都⽐较好
websockect的握⼿过程:
websockect实际只有⼀次HTTP的握⼿,服务端就能⼀直与客户端保持通信,直到关闭连接,接下来就来解析这⼀次握⼿的细节
1,服务器开启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()
2,客户端有⽐较完善的Websocket类库供我们使⽤,于是我们在客户端直接建⽴连接,并开始握⼿
var socket = new WebSocket("ws://127.0.0.1:8002/");
前端的Websocket类实例化后为我们⼲了⼀些事:
a. 不仅连接还会向服务器端发送握⼿信息,握⼿信息是byte类型,⼀个HTTP协议报⽂,⾥⾯还包含有Sec-WebSocket-Key 这个随机字符串
b. 负责接收服务器端返回的加密数据并进⾏验证,从⽽完成握⼿
这⾥是握⼿信息:
GET /chatsocket HTTP/1.1
Host: 127.0.0.1:8002
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Upgrade: websocket
Origin: localhost:63342
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
3,服务器端接收到客户端的信息,并进⾏utf-8将byte转码,并解析⾥⾯的http请求报⽂,将 Sec-WebSocket-Key 这个字符串提取出来,将这个字符串和magic string进⾏拼接,(注:magic string是固定的,值为258EAFA5-E914-47DA-95CA-
C5AB0DC85B11),然后将拼接好的字符串⽤base64进⾏加密,(这些操作都是websocket协议定义死的,包括magic string值)将加密好的字符串发送给前端,前端将⾃⼰加密的和服务器发送过的对⽐,⼀致则握⼿成功,否则失败报错。
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = headers['Sec-WebSocket-Key'] + magic_string
ac = base64.b64encode(hashlib.de('utf-8')).digest())
websockect的数据收发过程:
⼀次握⼿成功后客户端和服务端就可以进⾏数据的收发了。数据的收发实质就是解包和封包的过程
1,获取客户端发送的数据【解包】
info = v(8096)
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:]
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)
解包过程
0123
01234567890123456789012345678901
+-+-+-+-+-------+-+-------------+-------------------------------+
|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| | |
socket编程聊天室基本流程+-+-+-+-+-------+-+-------------+---------------+
| 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 ... |
+---------------------------------------------------------------+
2,向客户端发送数据【封包】
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)
token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes
conn.send(msg)
return True
附:下⾯摘抄⽹上⼀段客户端和服务端的对话,形象的帮助我们理解websocket和ajax轮询的区别:ajax轮询
ajax轮询的原理⾮常简单,让浏览器隔个⼏秒就发送⼀次请求,询问服务器是否有新信息。
场景再现:
客户端:啦啦啦,有没有新信息(Request)
服务端:没有(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response) —- loop
Websocket
客户端:啦啦啦,我要建⽴Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:⿇烦你有信息的时候推送给我噢。。
服务端:ok,有的时候会告诉你的。
服务端:balabalabalabala
服务端:balabalabalabala
服务端:哈哈哈哈哈啊哈哈哈哈
服务端:笑死我了哈哈哈哈哈哈哈
就变成了这样,只需要经过⼀次HTTP请求,就可以做到源源不断的信息传送了。(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我,⽽不是我傻乎乎的每次跑来问你 )
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论