【新特性之WebSocket】
详情参见:HTML5新特性之WebSocket
WebSocket协议是基于TCP的一种新的协议。WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信。
WebSocket是一种用于在服务器和客户端之间实现高效的双向通信的机制,通过WebSocket,能够实现在一个HTTP连接上自由地双向收发消息。
服务端与客户端的连接不断开,实现全双工的操作。及服务端或是客户端都会给对方发送消息。
|
1
2
3
4
5
6
|
WebSocket(内部还是socket) - 本质(magic string)魔法字符串,通过内部封装的通信加密规则,进行通信。
- 应用
- 连接
- 验证【握手信息】(magic string)
- 收发消息
|
一、启动服务端
|
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服务器后,等待用户【连接】,然后进行收发数据。
二、 客户端连接
客户端:浏览器 (必须有websocket包) 在浏览器中已经高度封装,url地址前引用标志:ws
|
1
2
3
4
5
6
7
8
|
#客户端 通过websocket 创建一个对象,完成的是三个操作!#创建连接#发送消息#接收验证消息<script type="text/javascript">
var socket = new WebSocket("ws://127.0.0.1:8002/xxoo");
...
</script> |
当客户端向服务端发送连接请求时,不仅连接还会发送【握手】信息,并等待服务端响应,至此连接才创建成功!
三、建立连接【握手】
操作对象:服务端, socket
|
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:***的键值对,用于验证服务端有没有websocket通信的功能。这个功能的验证就是判断服务端能不能按照归则对这段字符串进行加密【叫做握手信息】。加密之后的字符串【握手信息】返回给客户端进行解密,进行通信认证。
加密规则:hmac1,base64加密,+ magic string
请求【握手】信息为:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
GET /chatsocket HTTP/1.1
Host: 127.0.0.1:8002
Connection: UpgradePragma: no-cache
Cache-Control: no-cache
Upgrade: websocketOrigin: http://localhost:63342
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
...... |
请求和响应的【握手】信息需要遵循规则:
从请求【握手】信息中提取 Sec-WebSocket-Key
利用magic_string 和 Sec-WebSocket-Key 进行hmac1加密,再进行base64加密
将加密结果响应给客户端
注:magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
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')) ... ... ...