zoukankan      html  css  js  c++  java
  • websocket

    一、websocket

    1.1简介

    """
    网络协议
    	HTTP  			不加密传输
    	HTTPS				加密传输	
    	上面两个协议都是短连接,也就是完成一次请求与响应就会断开
    
    	websocket		加密传输
    	浏览器与服务端建立连接之后默认不断开,两端都可以基于该链接收发消息
    	websocket协议诞生真正意义上实现了服务端给客户端推送消息
    """
    

    1.2内部原理

    1.2.1原理

    """
    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代码验证

    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('
    
    ', 1)
        header_list = header.split('
    ')
        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
    " 
          "Upgrade:websocket
    " 
          "Connection: Upgrade
    " 
          "Sec-WebSocket-Accept: %s
    " 
          "WebSocket-Location: ws://127.0.0.1:8080
    
    "
    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)
    
    <script>
        var ws = new WebSocket('ws://127.0.0.1:8080/');
        // 这一行代码干了很多事
        // 1.自动生成随机字符串
        // 2.自动处理随机字符串 magic string  sha1/base64
        // 3.自动比对
    </script>
    

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

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

    python三大主流web框架对websocket的支持
    
    django
    	默认不支持
    	第三方模块:channels
    	
    
    flask
    	默认不支持
    	第三方模块:geventwebsocket
    
    
    tornado
    	默认就支持
    

    二、django实现websocket

    2.1注意事项

    """
    强调:
    	并不是所有的后端框架默认都支持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中进行如下注册:

    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中进行如下配置

    ASGI_APPLICATION = 'xxx.routing.application'
    # ASGI_APPLICATION = '项目名同名的文件名称.routing.py文件名.application变量名'
    

    2.2.3项目名同名的文件夹下创建routing.py文件并书写固定代码

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

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

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

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

  • 相关阅读:
    git上传本地项目
    第十一章 持有对象
    java 闭包与回调
    类名.class 类名.this 详解
    匿名内部类
    第十章 内部类
    Java简单调用Zookeeper服务
    Linux下ZooKeeper集群安装
    Linux自动化安装JDK
    linux下初步实现Keepalived+Nginx高可用
  • 原文地址:https://www.cnblogs.com/ghylpb/p/12505451.html
Copyright © 2011-2022 走看看