前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >腾讯云CDN支持WebSocket

腾讯云CDN支持WebSocket

原创
作者头像
yaho
发布2020-05-02 14:25:48
23.6K0
发布2020-05-02 14:25:48
举报
文章被收录于专栏:CDN杂谈

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('\r\n\r\n')[0].split('\r\n')
    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 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" % (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头部内容如下:

tcpdump抓包
tcpdump抓包
HTTP请求+响应头部
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访问
CDN访问

可以看到腾讯云CDN确实无需特殊配置即可支持WebSocket访问并透传源站。有一点在实际使用过程中需要注意的是节点默认支持10s的保活时间,10s内如果没有消息传递,将默认关闭连接。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
内容分发网络 CDN
内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档