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

websocket

作者头像
GH
发布2020-03-19 09:10:55
2.9K0
发布2020-03-19 09:10:55
举报

一、websocket

1.1简介

代码语言:javascript
复制
"""
网络协议
  HTTP            不加密传输
  HTTPS               加密传输    
  上面两个协议都是短连接,也就是完成一次请求与响应就会断开

  websocket       加密传输
  浏览器与服务端建立连接之后默认不断开,两端都可以基于该链接收发消息
  websocket协议诞生真正意义上实现了服务端给客户端推送消息
"""

1.2内部原理

1.2.1原理
代码语言:javascript
复制
"""
websocket内部原理大致可以分为两部分

1.握手环节:验证服务端是否支持websocket协议
  浏览器访问服务端
      浏览器会自动生成一个随机字符串,然后将该字符串自己保留一份给服务端也发送一份,这一阶段的数据交互是基于HTTP协议的(该随机字符串是放在请求头中的)
如下:
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: Upgrade
...
Sec-WebSocket-Key: kQHq6MzLH7Xm1rSsAyiD8g==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

      
  浏览器和服务端手上都有随机字符串
      服务端从请求头中获取随机字符串之后,会先拿该字符串跟magic string(固定的随机字符串)做字符串的拼接,会对拼接之后的数据进行加密处理(sha1/base64)  于此同时浏览器那边也会做相同的操作
  
  服务端将处理好的随机字符串再次发送给浏览器(响应头)
  
  浏览器会比对自己生成的随机字符串和服务端发送的随机字符串是否一致,如果一直说明支持websocket协议,如果不一致则会报错不支持

2.收发数据:密文传输 数据解密
  ps:
      1.基于网络传输 数据都是二进制格式(python中bytes类型)
      2.单位换算 
  数据解密
      1.先读取第二个字节的后七位二进制数(payload)
      
      2.根据payload不同做不同的处理
          =127:继续读8个字节
          =126:继续读2个字节
          <=125:不再往后读取
      
      3.往后读取固定长度的4个字节的数据(masking-key)
          根据该值计算出真实数据
"""
# 这些原理了解即可 关键需要说出几个关键字
  握手环节
  magic string  sha1/base64
  127、126、125
  payload masking-key

uploading-image-562514.png

1.2.2代码验证
代码语言:javascript
复制
import socket
import hashlib
import base64

# 正常的socket代码
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 防止mac/linux在重启的时候 报端口被占用的错
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)


sock.bind(('127.0.0.1', 8080))
sock.listen(5)

conn, address = sock.accept()
data = conn.recv(1024)  # 获取客户端发送的消息

def get_headers(data):
    """
    将请求头格式化成字典
    :param data:
    :return:
    """
    header_dict = {}
    data = str(data, encoding='utf-8')

    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

def get_data(info):
    """
    按照websocket解密规则针对不同的数字进行不同的解密处理
    :param info:
    :return:
    """
    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')

    return body

header_dict = get_headers(data)  # 将一大堆请求头转换成字典数据  类似于wsgiref模块
client_random_string = header_dict['Sec-WebSocket-Key']  # 获取浏览器发送过来的随机字符串

magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'  # 全球共用的随机字符串 一个都不能写错
value = client_random_string + magic_string  # 拼接

ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())  # 加密处理

# 响应头
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://127.0.0.1:8080\r\n\r\n"
response_str = tpl %ac.decode('utf-8')  # 处理到响应头中

# 将随机字符串给浏览器返回回去
conn.send(bytes(response_str, encoding='utf-8'))


# 收发数据
while True:
      data = conn.recv(1024)
      # print(data)

      value = get_data(data)

      print(value)
代码语言:javascript
复制
<script>
    var ws = new WebSocket('ws://127.0.0.1:8080/');
    // 这一行代码干了很多事
    // 1.自动生成随机字符串
    // 2.自动处理随机字符串 magic string  sha1/base64
    // 3.自动比对
</script>

这是内部原理,我们实际生产不需要写这些代码,直接使用封装好的模块即可

1.3主流web框架对websocket的支持情况

代码语言:javascript
复制
python三大主流web框架对websocket的支持

django
    默认不支持
    第三方模块:channels
    

flask
    默认不支持
    第三方模块:geventwebsocket


tornado
    默认就支持

二、django实现websocket

2.1注意事项

代码语言:javascript
复制
"""
强调:
    并不是所有的后端框架默认都支持websocket

在django中如果你想要开发websocket相关的功能,需要安装模块
    pip3 install channels==2.3
    注意事项
        1.不要直接安装最新版本的channels,这样可能会自动将你的django版本升级为最新版
        2.python解释器环境建议使用3.6(官网的说法:3.5可能会出现问题,3.7也可能会出现问题...具体说明问题官网没有说!)
    
channels内部已经帮你封装好了上面代码演示的所有的过程
    握手 加密 解密


"""

2.2实现步骤

2.2.1注册channels

在settings.py中进行如下注册:

代码语言:javascript
复制
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'channels'
]

这时启动django项目会报错CommandError: You have not set ASGI_APPLICATION, which is needed to run the server.

下面进行参数配置。

2.2.2配置参数

在settings.py中进行如下配置

代码语言:javascript
复制
ASGI_APPLICATION = 'xxx.routing.application'
# ASGI_APPLICATION = '项目名同名的文件名称.routing.py文件名.application变量名'
2.2.3项目名同名的文件夹下创建routing.py文件并书写固定代码
代码语言:javascript
复制
from channels.routing import ProtocolTypeRouter,URLRouter


application = ProtocolTypeRouter({
    'websocket':URLRouter([
        # websocket相关的url与视图函数对应关系
    ])
})

上述三步配置完成后,再次启动django,就会即支持http协议又支持websocket协议

之后关于http的url与视图函数对应关系还是在原来的urls.py中书写

关于websocket的url与视图函数对应关系则在routing.py中书写

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-03-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、websocket
    • 1.1简介
      • 1.2内部原理
        • 1.2.1原理
        • 1.2.2代码验证
      • 1.3主流web框架对websocket的支持情况
      • 二、django实现websocket
        • 2.1注意事项
          • 2.2实现步骤
            • 2.2.1注册channels
            • 2.2.2配置参数
            • 2.2.3项目名同名的文件夹下创建routing.py文件并书写固定代码
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档