你真的了解WebSocket吗?

2021-05-13 04:28

阅读:366

标签:套接字api   需要   rom   oca   tcp   base   decode   str   safari   

   WebSocket协议是基于TCP的一种新的协议。WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信。

 本文将使用Python编写Socket服务端,一步一步分析请求过程!!!

1. 启动服务端

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((127.0.0.1, 8002))
sock.listen(5)
# 等待用户连接
conn, address = sock.accept()
print(‘我来了‘,conn)

启动Socket服务器后,等待用户【连接】,然后进行收发数据。

2. 客户端连接

  

当客户端向服务端发送连接请求时,不仅连接还会发送【握手】信息,并等待服务端响应,至此连接才创建成功!  

我来了 laddr=(‘127.0.0.1‘, 8002), raddr=(‘127.0.0.1‘, 9089)>

3. 建立连接【握手】

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((‘127.0.0.1‘, 8002))
sock.listen(5)
# 等待用户连接
conn, address = sock.accept()
# print(‘我来了‘,conn)

# 接收信息 msg = conn.recv(8096) print(msg)
# msg的信息
GET / HTTP/1.1 Host: 127.0.0.1:8002 Connection: Upgrade Pragma: no-cache Cache-Control: no-cache User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 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,en;q=0.8 Cookie: m_lvt_b3a3fc356d0af38b811a0ef8d50716b8=1552050219;
      csrftoken=zMIqQhUHFPKDYrSfRKXlYziC48Hhr9gybHn5dhT1YCxjWWL0hpFpbhpEK2f1ZveI;
      OUTFOX_SEARCH_USER_ID_NCOO=365670935.15332246;
      session=eyJfcGVybWFuZW50Ijp0cnVlLCJ1c2VyX2lkIjoxfQ.EKbEWg.e-R5XugNGe1_STfG3K8B3jqDTkk Sec-WebSocket-Key: NQ+slWNHq4Xy8xlVMKCNtg== Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

请求和响应的【握手】信息需要遵循规则:

  • 从请求【握手】信息中提取 Sec-WebSocket-Key
  • 利用magic_stringSec-WebSocket-Key 进行hmac1加密,再进行base64加密
  • 将加密结果响应给客户端

 注:magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11                     就是这个 不能修改

import socket
import base64
import hashlib


def get_headers(data):
    """
    将请求头格式化成字典
    :param data:
    :return:
    """
    header_dict = {}
    data = str(data, encoding=utf-8)
    # for i in data.split(‘\r\n‘):
    #     print(i)
    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


sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((127.0.0.1, 8002))
sock.listen(5)

# 1. 等待用户连接
conn, address = sock.accept()
# 2. 接收验证消息
data = conn.recv(1024)
headers = get_headers(data)  # 提取请求头信息

magic_string = 258EAFA5-E914-47DA-95CA-C5AB0DC85B11

# 3. 对数据加密
value = headers[Sec-WebSocket-Key] + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode(utf-8)).digest())

# 4. 将加密之后的数据返回
# 对请求头中的sec-websocket-key进行加密
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n"                "Upgrade:websocket\r\n"                "Connection: Upgrade\r\n"                "Sec-WebSocket-Accept: %s\r\n"                "WebSocket-Location: ws://%s%s\r\n\r\n"

response_str = response_tpl % (ac.decode(utf-8), headers[Host], headers[url])
conn.send(bytes(response_str, encoding=utf-8))

# 5. 接收用户传过来的信息
while True:
    msg = conn.recv(8096)
    print(msg)

在浏览器中的console中 发送

ws.send(123445)

# 这个时候在前端就会收到 加密之后的msg
b"\x81\x86\x0c‘W*=\x15d\x1e8\x12"

我们得到的是一串加密的数据 我们怎么才可以读懂呢?

4.客户端和服务端收发数据

客户端和服务端传输数据时,需要对数据进行【封包】和【解包】。客户端的JavaScript类库已经封装【封包】和【解包】过程,但Socket服务端需要手动实现。

解包详细过程: 

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 ...                |
+---------------------------------------------------------------+

 

我们拿到加密后的数据
b"\x81\x86\x0c‘W*=\x15d\x1e8\x12"
我们可以看到上面的数字
 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

就是我们切割拿到的加密数据的第二位 与 (01111111)127做  & (位)运算  得到的结果 分三种情况

如果是127:
  b"\x81\x86\x0c‘W*=\x15d          \x1e8\x12"
如果是126:
  b"\x81\x86\x0c‘W*=               \x15d\x1e8\x12"
如果是小于等于125:
  b"\x81\x86                        \x0c‘W*=\x15d\x1e8\x12"

  

  

第一步:获取客户端发送的数据【解包】

 

 

 

 

 

你真的了解WebSocket吗?

标签:套接字api   需要   rom   oca   tcp   base   decode   str   safari   

原文地址:https://www.cnblogs.com/a438842265/p/12003087.html


评论


亲,登录后才可以留言!