WebSocket协议是基于TCP的一种新的协议。WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信。
本文将使用Python编写Socket服务端,一步一步分析请求过程!!!
1. 启动服务端
?
1 2 3 4 5 6 7 8 9 10 | 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服务器后,等待用户【连接】,然后进行收发数据。
2. 客户端连接
?
1 2 3 4 | <script type = "text/javascript" >
var socket = new WebSocket( "ws://127.0.0.1:8002/xxoo" );
... < / script> |
当客户端向服务端发送连接请求时,不仅连接还会发送【握手】信息,并等待服务端响应,至此连接才创建成功!
3. 建立连接【握手】
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 ) # 获取客户端socket对象 conn, address = sock.accept() # 获取客户端的【握手】信息 data = conn.recv( 1024 ) ... ... ... conn.send( ‘响应【握手】信息‘ ) |
请求和响应的【握手】信息需要遵循规则:
- 从请求【握手】信息中提取Sec-WebSocket-Key
- 利用magic_string 和 Sec-WebSocket-Key 进行hmac1加密,再进行base64加密
- 将加密结果响应给客户端
注:magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
请求【握手】信息为:
+ View Code?
1 2 3 4 5 6 7 8 9 10 11 12 | GET / chatsocket HTTP / 1.1 Host: 127.0 . 0.1 : 8002 Connection: Upgrade Pragma: no - cache Cache - Control: no - cache Upgrade: websocket Origin: http: / / localhost: 63342 Sec - WebSocket - Version: 13 Sec - WebSocket - Key: mnwFxiOlctXFN / DeMt1Amg = = Sec - WebSocket - Extensions: permessage - deflate; client_max_window_bits ... ... |
提取Sec-WebSocket-Key值并加密:
+ View Code?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | import socket import base64 import hashlib 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 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() data = conn.recv( 1024 ) headers = get_headers(data) # 提取请求头信息 # 对请求头中的sec-websocket-key进行加密 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" magic_string = ‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11‘ value = headers[ ‘Sec-WebSocket-Key‘ ] + magic_string ac = base64.b64encode(hashlib.sha1(value.encode( ‘utf-8‘ )).digest()) response_str = response_tpl % (ac.decode( ‘utf-8‘ ), headers[ ‘Host‘ ], headers[ ‘url‘ ]) # 响应【握手】信息 conn.send(bytes(response_str, encoding = ‘utf-8‘ )) ... ... ... |
4.客户端和服务端收发数据
客户端和服务端传输数据时,需要对数据进行【封包】和【解包】。客户端的JavaScript类库已经封装【封包】和【解包】过程,但Socket服务端需要手动实现。
第一步:获取客户端发送的数据【解包】
??info = conn.recv(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:] ???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)
解包详细过程:
+ View Code?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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| | | + - + - + <知识推荐
我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8
不良信息举报平台
互联网安全管理备案
Copyright 2023 www.wodecom.cn All Rights Reserved |