• python--websocket数据解析


    # websocket实现原理
    '''
    1.服务端开启socket,监听ip和端口
    2.客户端发送连接请求(带上ip和端口)
    3.服务端允许连接
    4.客户端生成一个随机字符串,和magic string组合进行一个sha1加密,加密。并将随机字符串发送给服务端
    5.然后服务端也要用相同的方式进行加密。
    6.然后服务端将加密之后的密串返回给客户端
    7.客户端将服务端返回的密串和自己加密之后的密串进行比对,如果一样,说明遵循同样的协议。如果不一样,就没法玩了·····
    '''
    
    import socket
    import base64
    import hashlib
    from pprint import pprint
    
    
    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
    
    
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('localhost', 8080))
    server.listen(5)
    
    # 等待用户连接
    conn, addr = server.accept()
    
    # 握手消息
    content = conn.recv(1024)
    '''
    >>> print(content)
    
    b'GET / HTTP/1.1
    Host: localhost:8080
    Connection: Upgrade
    Pragma: no-cache
    Cache-Control: no-cache
    Upgrade: websocket
    Origin: http://localhost:63342
    Sec-WebSocket-Version: 13
    User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9
    Cookie: uuid=81a68694c772e0c62d4a5a3c256fe3e0; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2216453a8bf2bbe-09a40e8e58a866-5e442e19-1fa400-16453a8bf2c745%22%7D; Hm_lvt_2af69bc2b378fb58ae04ed2a04257ed1=1530411925; Pycharm-bdfc5fce=a920e49d-da4e-4d2f-a76e-17acfacc6462
    Sec-WebSocket-Key: 1y6WpsSgfF80wqi3HpmrqQ==
    Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
    
    '
    '''
    
    # 获取请求头
    headers = get_headers(content)
    '''
    >>>pprint(headers)
    {'Cache-Control': 'no-cache',
     'Connection': 'Upgrade',
     'Cookie': 'Pycharm-bdfc5fce=a920e49d-da4e-4d2f-a76e-17acfacc6462',
     'Host': 'localhost:8080',
     'Origin': 'http://localhost:63342',
     'Sec-WebSocket-Key': 'RRGDeYeYSGEP9eHy85u8oQ==',
     'Sec-WebSocket-Version': '13',
     'Upgrade': 'websocket',
     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) '
                   'like Gecko',
     'method': 'GET',
     'protocol': 'HTTP/1.1',
     'url': '/'}
    '''
    
    # 规定:魔法字符串就叫这个
    magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
    
    # 获取随机密串,并将其与魔法字符串组合
    value = headers["Sec-WebSocket-Key"] + magic_string
    
    # 进行加密,规定,只能按照此加密方式
    hash_str = base64.b64encode(hashlib.sha1(bytes(value, encoding='utf-8')).digest())
    
    response_tpl = "HTTP/1.1 101 Switching Protocols
    " 
                   "Upgrade:websocket
    " 
                   "Connection: Upgrade
    " 
                   "Sec-WebSocket-Accept: %s
    " 
                   "WebSocket-Location: ws://%s%s
    
    "
    
    # 获取握手消息,组合魔法字符串,进行sha1加密
    # 发送给客户端
    response_str = response_tpl % (str(hash_str, encoding='utf-8'), headers['Host'], headers['url'])
    conn.send(bytes(response_str, encoding='utf-8'))
    
    
    # 也可以发送数据
    def send_msg(conn, msg_bytes):
        """
        WebSocket服务端向客户端发送消息
        :param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
        :param msg_bytes: 向客户端发送的字节
        :return:
        """
        import struct
    
        token = b"x81"
        length = len(msg_bytes)
        if length < 126:
            token += struct.pack("B", length)
        elif length <= 0xFFFF:
            token += struct.pack("!BH", 126, length)
        else:
            token += struct.pack("!BQ", 127, length)
    
        msg = token + msg_bytes
        conn.send(msg)
        return True
    
    # 那么便可以接受数据了,注意接受的数据必须按照一定的规则才能够获取
    while True:
        info = conn.recv(1024)
        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')
        print(body)
    
        # 可以将获取的body加上新的字符
        body = body + "我是你爸"
        send_msg(conn, bytes(body, encoding="utf-8"))
    

      

      解包过程:(这个图来自于武sir老师的博客:https://www.cnblogs.com/wupeiqi/p/6558766.html)

    0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-------+-+-------------+-------------------------------+
    |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
    |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
    |N|V|V|V|       |S|             |   (if payload len==126/127)   |
    | |1|2|3|       |K|             |                               |
    +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
    |     Extended payload length continued, if payload len == 127  |
    + - - - - - - - - - - - - - - - +-------------------------------+
    |                               |Masking-key, if MASK set to 1  |
    +-------------------------------+-------------------------------+
    | Masking-key (continued)       |          Payload Data         |
    +-------------------------------- - - - - - - - - - - - - - - - +
    :                     Payload Data continued ...                :
    + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    |                     Payload Data continued ...                |
    +---------------------------------------------------------------+
    

      

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <script>
            ws = new WebSocket("ws://localhost:8080/");
    
            //如果连接成功,会打印下面这句话,否则不会打印
            ws.onopen = function () {
                console.log('连接成功')
            };
    
            //接收数据,服务端有数据过来,会执行
            ws.onmessage = function (event) {
                console.log(event)
            };
    
            //服务端主动断开连接,会执行.
            //客户端主动断开的话,不执行
            ws.onclose = function () {  }
    
        </script>
    </body>
    </html>
    

      

      

      

  • 相关阅读:
    java经常出现的异常
    后台采用springmvc框架 前台bootstrap 实现对话框编辑信息
    List集合与Array数组之间的互相转换
    freemarker 设置文本内容超过一定长度 用省略号代替
    bootstrap实现多个下拉框同时搜索
    jquery 循环遍历选中的多选复选框checkbox
    同时对数据库进行更新,添加与删除操作
    获取页面内容封装成json对象
    前台bootstrap按钮动态添加与删除
    set 遍历
  • 原文地址:https://www.cnblogs.com/traditional/p/9260830.html
Copyright © 2020-2023  润新知