zoukankan      html  css  js  c++  java
  • websocket以及它的内部原理

    websocket(主流浏览器都支持)

    """
    网络协议 
    	HTTP  不加密传输
    	HTTPS  加密传输
    		上面两个都是短链接/无链接
    	WebSocket  加密传输
    		浏览器和服务端创建链接之后默认不断开(联想网络编程TCP recv和send方法)
    		它的诞生能够真正的实现服务端给客户端推送消息
    """
    

    内部原理(重点)

    """
    websocket实现原理可以分为两部分
    	1.握手环节(handshake):并不是所有的服务端都支持websocket 所以用握手环节来验证服务端是否支持websocket
    	2.收发数据环节:数据解密
    """
    
    """
    1.握手环节
    	浏览器访问服务端之后浏览器会立刻生成一个随机字符串
    	浏览器会将生成好的随机字符串发送给服务端(基于HTTP协议 放在请求头中),并且自己也保留一份
    	
    	服务端和客户端都会对该随机字符串做以下处理
    	1.1 先拿随机字符串跟magic string(固定的字符串)做字符串的拼接
    	1.2 将拼接之后的结果做加密处理(sha1+base64)
    	
    	服务端将生成好的处理结果发送给浏览器(基于HTTP协议 放在响应头中)
    	浏览器接受服务端发送过来的随机字符串跟本地处理好的随机字符串做比对,如果一致说明服务端支持websocket,如果不一致说明不支持
    
    2.收发数据环节
    	前提知识点:
    		1.基于网络传输数据都是二进制格式 在python中可以用bytes类型对应
    		2.进制换算
    	
    	先读取第二个字节的后七位数据(payload) 根据payload做不同的处理
    	=127:继续往后读取8个字节数据(数据报10个字节)
    	=126:继续往后读取2个字节数据(数据报4个字节)
    	<=125:不再往后读取(数据2个字节)
    	
    	上述操作完成后,会继续往后读取固定长度4个字节的数据(masking-key)
    	依据masking-key解析出真实数据
    """
    # 关键字:magic string、sha1/base64、payload(127,126,125)、masking-key
    

    代码验证(无需掌握)

    # 请求头中的随机字符串
    Sec-WebSocket-Key: NlNG/FK/FrQS/RH5Bcy9Gw==
    # 响应头
    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')  # 处理到响应头中
    
    import socket
    import hashlib
    import base64
    
    # 正常的socket代码
    sock = socket.socket()  # 默认就是TCP
    # 避免mac本重启服务经常报地址被占用的错误
    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)  # 获取客户端发送的消息
    # print(data.decode('utf-8'))
    
    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')  # 处理到响应头中
    
    
    # 基于websocket收发消息
    conn.send(bytes(response_str,encoding='utf-8'))
    
    while True:
        data = conn.recv(1024)
        # print(data)  # 加密数据 b'x81x89
    x94xac#xee)x0cxc6xaf)Ixb6x80'
        value = get_data(data)
        print(value)
    
    <script>
        var ws = new WebSocket('ws://127.0.0.1:8080/')
        // 这一句话帮你完成了握手环节所有的操作
        // 1 生成随机字符串
        // 2 对字符串做拼接和加码操作
        // 3 接受服务端返回的字符串做比对
    </script>
    

    总结:上述代码知识为了诠释websocket内部本质,实际应用直接使用别人封装好的模块即可

    实际应用中,并不是所有的后端框架默认都支持websocket协议,如果你想使用的话,可能需要借助于不同的第三方模块

  • 相关阅读:
    hive使用derby的服务模式(可以远程模式)
    使用sqoop过程
    使用mongodump及mongorestore备份及恢复数据
    sql server 2000 错误229 拒绝了对象sysobjects 的select 权限
    node.js 连接 sql server 包括低版本的sqlserver 2000
    centos7 安装jdk1.8
    mybatis配置时出现org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)
    该网页已屏蔽以下插件Adobe Flash Player
    关于IntelliJ IDEA 文档无法编辑的解决办法
    华硕_ZX50JX4200 安装ssd固态盘
  • 原文地址:https://www.cnblogs.com/yafeng666/p/12691969.html
Copyright © 2011-2022 走看看