zoukankan      html  css  js  c++  java
  • websocket 原理

    自己写一个websocket

    原理更多参考--   https://www.cnblogs.com/zunzunQ/p/8549180.html
    原理简介:
        client端 握手请求中 包含一个base64 encode的随机字符串,
        server端 magic_string+str    sha1摘要之后base64加密, 发给client 
        服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦  (回调机制)
    import socket, hashlib, base64
    
    sock = socket.socket()
    sock.bind(('127.0.0.1', 9000))
    sock.listen(5)
    '''
    b'GET / HTTP/1.1
    
    Host: 127.0.0.1:9000
    
    Connection: Upgrade
    
    Pragma: no-cache
    
    Cache-Control: no-cache
    
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36
    
    Upgrade: websocket
       --建立--
    Origin: http://localhost:63342
    
    Sec-WebSocket-Version: 13
    
    Accept-Encoding: gzip, deflate, br
    
    Accept-Language: zh-CN,zh;q=0.9
    
    Sec-WebSocket-Key: vNtQ8bVSNRPL5p+xo1X5CA==
      验证 一按钥匙
    Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
    
    
    '''
    '''
    b'GET / HTTP/1.1
       -- 单纯http地址栏访问
    Host: 127.0.0.1:9000
    
    Connection: keep-alive
    
    Pragma: no-cache
    
    Cache-Control: no-cache
    
    Upgrade-Insecure-Requests: 1
    
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36
    
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    
    Accept-Encoding: gzip, deflate, br
    
    Accept-Language: zh-CN,zh;q=0.9
    
    '
    '''
    conn, addr = sock.accept()
    data = conn.recv(1024)
    # print(data)
    
    def get_header(data): # 获取头部信息
        header_dict = {}
        header_str = data.decode('utf8')
        for i in header_str.split('
    '):
            if str(i).startswith('Sec-WebSocket-Key'):
                header_dict['Sec-WebSocket-Key'] = i.split(':')[1].strip()
        return header_dict
    
    
    headers = get_header(data)
    magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'  # magic_string = 魔法字符串
    value = headers['Sec-WebSocket-Key'] + magic_string
    print(value)  #mzrsVOiYogmzB8fYcyMBpw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
    ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())  # 固定加密算法
    print(ac) #b'0Cu1dAA7U6tFsJS4EVkIDLcvKDU='
    response_tpl = "HTTP/1.1 101 Switching Protocols
    " 
                   "Upgrade:websocket
    " 
                   "Connection: Upgrade
    " 
                   "Sec-WebSocket-Accept: %s
    " 
                   "WebSocket-Location: ws://127.0.0.1:9527
    
    "
    response_str = response_tpl % (ac.decode('utf-8'))
    conn.send(response_str.encode('utf8'))  # 响应握手信息
    
    while True:
        msg = conn.recv(8096)
        print(msg)

    解密过程  分析

    hashstr = b'x81x89xbcxb8,xb5Y6xa5Px12x0bxc8x0f:'
    # b'x81    x83    xceHxb6x85xffzx85'
    
    # 将第二个字节也就是 x83 第9-16位 进行与127进行 与位 运算
    payload = hashstr[1] & 127
    print(payload)
    if payload == 127:
        extend_payload_len = hashstr[2:10]  # 顾头不顾尾
        mask = hashstr[10:14]
        decoded = hashstr[14:]
    # 当位运算结果等于127时,则第3-10个字节为数据长度
    # 第11-14字节为mask 解密所需字符串
    # 则数据为第15字节至结尾
    
    if payload == 126:
        extend_payload_len = hashstr[2:4]
        mask = hashstr[4:8]
        decoded = hashstr[8:]
    # 当位运算结果等于126时,则第3-4个字节为数据长度  255*255+510=65535 21845汉字
    # 第5-8字节为mask 解密所需字符串
    # 则数据为第9字节至结尾
    
    if payload <= 125:
        extend_payload_len = None
        mask = hashstr[2:6]
        decoded = hashstr[6:]
    
    # 当位运算结果小于等于125时,则这个数字就是数据的长度
    # 第3-6字节为mask 解密所需字符串
    # 则数据为第7字节至结尾
    
    str_byte = bytearray()  # 字节流
    
    for i in range(len(decoded)):
        byte = decoded[i] ^ mask[i % 4]  # 异或运算  上下为0就是0  上下有1就是1
        str_byte.append(byte)
    
    print(str_byte.decode("utf8"))  # 主要解决中文  厉害了
    
    # # 1字节 = 8 bit  与位运算
    # 01111111
    # 11111111  最大255
    # 2/4/8/16/32/64/138/256

    加密过程 -- 分析

    import struct
    msg_bytes = "hello".encode("utf8")  #转bytes
    token = b"x81"       # 数据标准格式
    length = len(msg_bytes)  #第二位就是数据长度
    
    if length < 126:
        token += struct.pack("B", length)
    elif length == 126:
        token += struct.pack("!BH", 126, length)
    else:
        token += struct.pack("!BQ", 127, length)
    
    msg = token + msg_bytes
    
    print(msg)

    总结

    WebSocket的工作原理
    1.握手
        客户端   Sec-WebSocket-Key
        响应     base64(sha1(Sec-WebSocket-Key + magic_string))
        
    2.解密:
        与127 进行""位运算 结果是两个数的最小值
        1. == 127 第3-10个字节表示该数据的长度 
        2. == 126 第3-4个字节表示该数据的长度 00000010 00000000
        3. <= 125 当前这个数字就是Websocket发送的数据长度
        
  • 相关阅读:
    例子:动能并不是特别强(2-3)后,下M5的同时,也是恢复期到期的前一天
    .NET 自带的动态代理+Expression 实现AOP
    自旋锁-SpinLock(.NET 4.0+)
    使用Nito.AsyncEx实现异步锁
    C# 两行代码实现 延迟加载的单例模式(线程安全)
    C++ 用于大型程序的工具
    C++ 模板与泛型编程
    C++ 面向对象编程
    C++ 重载操作符与转换
    C++ 复制控制
  • 原文地址:https://www.cnblogs.com/zhangchen-sx/p/10587798.html
Copyright © 2011-2022 走看看