1. WebSocket简介
Websocket是用于服务端主动向客户端推送消息的技术。传统的HTTP/HTTPS只能由客户端向服务端发起请求,服务端对请求一一响应。在需要获取服务端状态变化的场景下,如:提交的后台任务是否执行成功,只能通过客户端轮询向服务端发起请求,不仅效率低,还浪费资源(HTTP1.0下每次轮询都需要经过TCP三次握手重新建立连接)。而WebSocket的出现较好的解决了这个问题,在TCP首次建立完连接之后,该连接不自动关闭,在有效期内客户端可以继续向服务端发送消息,服务端也能主动给客户端发送消息。
2. 腾讯云CDN对WebSocket的支持
腾讯云CDN依靠全球广泛部署的CDN节点,高效的网络存储优化方案和精准的调度策略,有效提升下载速度、降低响应时间,提供流畅的用户体验。腾讯云CDN节点自研服务器在提供静态资源访问的能力下,同时支持WebSocket访问,兼容动态资源的极速上云服务。本文将介绍如果验证腾讯云CDN节点支持WebSocket。
2.1 配置源站支持WebSocket
由于WebSocket属于动态资源,不适用于缓存服务,所有请求必定回源,所以首先需要源站支持WebSocket。以下以Python Nginx为例介绍如何配置代理支持WebSocket。
- 2.1.1 Websocket服务端
服务端示例代码:
代码语言:javascript复制#!/usr/bin/python
#coding=utf8
"""
# Created Time : 2020-05-02 11:07:02
# File Name: websocket-server.py
# Description:
"""
import socket
import base64
import hashlib
import struct
from threading import Thread
def parse_headers(data):
headers = {}
method = url = protocol = ''
raw_headers = data.split('rnrn')[0].split('rn')
for i in range(0, len(raw_headers)):
if i == 0:
elements = raw_headers[i].split(' ')
if len(elements) == 3:
method, url, protocol = elements
else:
k, v = raw_headers[i].split(':', 1)
headers[k] = v.strip()
print headers
return method, url, protocol, headers
def calc_sign(data):
message = data '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' #不要问为什么是这个字符串,rfc6455中定义的!
return base64.b64encode(hashlib.sha1(message).digest())
def encode(data):
token = b"x81"
length = len(data)
if length < 126:
token = struct.pack("B", length)
elif length <= 0xFFFF:
token = struct.pack("!BH", 126, length)
else:
token = struct.pack("!BQ", 127, length)
return token data
def decode(raw_data):
payload_len = ord(raw_data[1]) & 127
if payload_len == 126:
extend_payload_len = raw_data[2:4]
mask = raw_data[4:8]
decoded = raw_data[8:]
elif payload_len == 127:
extend_payload_len = raw_data[2:10]
mask = raw_data[10:14]
decoded = raw_data[14:]
else:
extend_payload_len = None
mask = raw_data[2:6]
decoded = raw_data[6:]
data = bytearray()
for i in range(len(decoded)):
chunk = ord(decoded[i]) ^ ord(mask[i % 4])
data.append(chunk)
return str(data)
def handle_request(conn):
data = conn.recv(1024)
print data
method, url, protocol, headers = parse_headers(data)
response = "HTTP/1.1 101 Switching Protocolsrn"
"Upgrade:websocketrn"
"Connection:Upgradern"
"Sec-WebSocket-Accept:%srn"
"WebSocket-Location:ws://%s%srnrn" % (calc_sign(headers['Sec-WebSocket-Key']), headers['Host'], url)
conn.send(response)
while True:
try:
raw_data = conn.recv(1024)
except Exception as e:
raw_data = None
if not raw_data:
break
data = decode(raw_data)
print('recv data', data)
if not conn.send(encode(data)):
break
def run():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('0.0.0.0', 8888))
sock.listen(10)
while True:
conn, address = sock.accept()
print('new client %s:%s' % address)
Thread(target=handle_request, args=(conn,)).start()
sock.close()
if __name__ == '__main__':
run()
该服务通过socket响应客户端连接,打印出每个连接的头部,读取客户端发送的消息,并将其发回给客户端。注意websocket通信发送的不是原始消息,需要经过编码,如encode,decode函数。
- 2.1.2 Websocket客户端
客户端同样需要编解码,也可以直接利用现有工具简化测试步骤:https://pypi.org/project/websocket_client/
- 2.1.3 本地测试
分两次分别发送两次消息,123和456。
请求的抓包HTTP头部内容如下:
可以看到服务端总共只收到了一个TCP建立连接请求,一组HTTP头部,两组消息共用一个连接。websocket实际上发送和响应的也是标准的http头部格式,只是额外带上了鉴权头部。
- 2.1.4 配置Nginx代理WebSocket
一般我们会在最终的服务前面挂一个代理服务支持路由 负载均衡等,如Nginx等。Nginx需要启用额外配置支持Websocket,修改Nginx配置文件配置如下:
代码语言:javascript复制worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
resolver 119.29.29.29;
# websocket
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream websocket {
server 127.0.0.1:8888;
}
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name _;
root /usr/local/nginx/nginx/http/;
access_log logs/all-http.log;
# websocket
location ~ ^/websocket {
proxy_pass http://websocket;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For
$proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
}
重启nginx服务:
代码语言:javascript复制nginx -s reload
客户端重新请求80端口即可。
代码语言:javascript复制wsdump.py ws://127.0.0.1:80/websocket
- 2.1.5 测试CDN支持WebSocket
在腾讯云CDN控制台域名配置页面将域名源站设置为支持websocket的源站,待配置生效后,直接通过WebSocket协议访问。
可以看到腾讯云CDN确实无需特殊配置即可支持WebSocket访问并透传源站。有一点在实际使用过程中需要注意的是节点默认支持10s的保活时间,10s内如果没有消息传递,将默认关闭连接。