Python—Tornado框架(三)WebSocket原理

Python——Tornado框架(三)、WebSocket

参考博文:https://www.cnblogs.com/wupeiqi/p/6558766.html

一、WebSocket介绍

Http——socket实现,短链接。链接之后断开。只能请求响应。

WebSocket——socket实现,双工通道。想什么时候断就什么时候断。不仅请求响应,还能推送。

本质就是Socket创建链接,不断开。

知道了本质,就可以从socket入手:

二、WebSocket握手过程分析

  • 服务端(socket服务端)
    1、服务端开启socket,监听IP和端口
    3、允许链接
    *5、服务端接收到特殊值【特殊值加密,sha1加密,magic string=258EAFA5-E914-47DA-95CA-C5AB0DC85B11】(这个magic string 是固定值)
    6、加密后的值发送给客户端

  • 客户端(浏览器)
    2、客户端发起请求(IP和端口)
    *4、客户端生成一个特殊值,向服务端发送一段特殊值 “xxxxxxxxx”。切客户端自己加密这段特殊值,但不发送。
    7、客户端接收到加密的的值,与自己加密的值相比较,如果一样的话,说明遵循了WebSocket协议。

收发数据

三、基于Python实现WebSocket握手过程

由于是通过socket实现的,先简单的搭建:
后端:
在这里插入图片描述

前端:
在这里插入图片描述
可以看到后端部分data是接收信息的,可以先看下接收到的信息有什么:
在这里插入图片描述可以看到获取到了很多信息,可以发现很像 http 的请求头之类的东西。我们可以写一个函数对这段信息进行分隔:

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

分割后的信息:
在这里插入图片描述
接着我们就能看到生成的随机字符串:
在这里插入图片描述

既然获取到这个值,就可以对它进行加密:
在这里插入图片描述

接着:
在这里插入图片描述
发送给客户端之后,就要进行对比:
在这里插入图片描述

效果:
在这里插入图片描述

可以看到成功实现,如果把maigc_srring稍微改动一点,就可以看到链接失败:
在这里插入图片描述

四、WebSocket数据解析过程

1、客户端向服务端发送消息

客户端发送:
在这里插入图片描述
服务端接收:
在这里插入图片描述
然后结果:
在这里插入图片描述
可以看到这就是我们接收到的东西。

接收到数据以后,需要通过一定的方式才能看到信息。

2、数据解析

在这里插入图片描述
因为一个字节8位,所以前8个数字代表一个字节。
在这里插入图片描述
因为info接收到这些字节,所以 Info[0],就是拿第一个字节。要计算一个字节后四位的值,可以通过 跟 00001111 进行与运算,就能知道相对应的值。

(看上图)
所以 oncode = info[0] & 15
如果我们想要 fin (上图第一个)的值,可以通过拿到第一个字节,向右移动7位,就可以拿到:
fin = info[0] >>7

可以看到payload len占7位。(如果全是1,则最大是127)
所以 payload len的值为:
payload_len = info[1] & 127

重点:payload len的长度会决定往后占多少,后面占几位,payload len说了算。

在这里插入图片描述
如果payload len小于126,表示:
在这里插入图片描述

表示刚好就这么多位置,后面放数据。

如果payload len等于126:
再往后延伸16位(两个字节)

如果payload len大于126:
往后延伸64位(8个字节)

3、masking key

数据部分的前面4个字节,是masking key,发送的数据已经进行加密,需要通过前面4个字节进行解密。
数据头—masking key—数据

4、数据解包

既然知道了原理,就可以进行解包:

info = conn.recv(8096)

    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)

能分割开了,就能去解码了(原理):
在这里插入图片描述

后端:
在这里插入图片描述
然后再次看效果:
在这里插入图片描述
就能发现成功接收到数据。

如果字节是一个一个解,则只能解码英文。因为中文是三个三个字节。中文就不要直接解成字符串,中文先解成字节,最后全部都是字节,再统一转换成字符串。所以这里不仅能解码英文,还能解码中文:
在这里插入图片描述

5、客户端服务端互发消息

如果我们加上一个循环,就能不断接收:
在这里插入图片描述
在这里插入图片描述
后端:
在这里插入图片描述

然后服务端发送消息:
代码:
在这里插入图片描述在这里插入图片描述

前端:
在这里插入图片描述

效果:
在这里插入图片描述在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41824825/article/details/117267255