博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Websocket - Websocket原理(握手、解密、加密)、基于Python实现简单示例
阅读量:2240 次
发布时间:2019-05-09

本文共 8451 字,大约阅读时间需要 28 分钟。

一、Websocket原理(握手、解密、加密)

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

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

1、启动服务端

import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)sock.bind(('127.0.0.1', 9527))sock.listen(5)# 获取客户端socket对象,并等待用户连接conn, address = sock.accept()..................

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

2、客户端连接

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

3、建立连接【握手】

import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)sock.bind(('127.0.0.1', 9527))sock.listen(5)# 获取客户端socket对象,并等待用户连接conn, address = sock.accept()# 获取客户端的【握手】信息,data即握手信息data = conn.recv(1024)............conn.send('响应【握手】信息')

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

    - 从请求【握手】信息中提取 Sec-WebSocket-Key;

    - 利用magic_string 和 Sec-WebSocket-Key 进行hmac1加密,再进行base64加密;

    - 将加密结果响应给客户端;

  注:magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11

  请求【握手】信息为:

b'GET /xxoo HTTP/1.1\r\nHost: 127.0.0.1:9527\r\nConnection: Upgrade\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\r\nUpgrade: websocket\r\nOrigin: http://localhost:63342\r\nSec-WebSocket-Version: 13\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: session=ba01367c-59b9-41d4-81ba-30b70db282c6\r\nSec-WebSocket-Key: jLSLU57WxRJACRQxlN47Tw==\r\nSec-WebSocket-Extensions: permessage-deflate; ......'

  提取Sec-WebSocket-Key值并加密:

import socketimport base64import hashlibdef get_headers(data):    """    将请求头格式化成字典    :param data: 请求头    :return: 请求头信息字典    """    header_dict = {}    header_str = data.decode("utf8")    for i in header_str.split("\r\n"):        if str(i).startswith("Sec-WebSocket-Key"):            header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip()    return header_dictsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)sock.bind(('127.0.0.1', 9527))sock.listen(5)# 获取客户端socket对象,并等待用户连接conn, address = sock.accept()# 获取客户端的【握手】信息data = conn.recv(1024)headers = get_headers(data)  # 提取请求头信息# 对请求头中的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://127.0.0.1:9527\r\n\r\n"magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'value = headers['Sec-WebSocket-Key'] + magic_stringac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())response_str = response_tpl % (ac.decode('utf-8'))# 响应【握手】信息conn.send(response_str.encode("utf-8"))............

  至此,客户端与服务端完成握手过程。

4、客户端与服务端收发数据

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

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

# b'\x81\x86#\xa47\x93\xc7\x19\x97v\x86\x19'     你好hashstr = b'\x81\x83\xceH\xb6\x85\xffz\x85'# b'\x81  \x86#   \xa47\x93\xc7\x19    \x97v\x86\x19'# 将第二个字节 \x86# 也就是第9-16位 与 127 进行位运算payload_len = hashstr[1] & 127if payload_len == 127:    extend_payload_len = hashstr[2:10]    mask = hashstr[10:14]    decoded = hashstr[14:]# 当位运算结果等于127时,则第3-10个字节为数据长度# 第11-14字节为mask ,即解密所需字符串# 则数据为第15字节至结尾if payload_len == 126:    extend_payload_len = hashstr[2:4]    mask = hashstr[4:8]    decoded = hashstr[8:]# 当位运算结果等于126时,则第3-4个字节为数据长度# 第5-8字节为mask ,即解密所需字符串# 则数据为第9字节至结尾if payload_len <= 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]  # ^ 是或运算    str_byte.append(byte)print(str_byte.decode("utf8"))

  解包详细过程:

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

  第二步:向客户端发送数据【封包】

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 == 126:        token += struct.pack("!BH", 126, length)    else:        token += struct.pack("!BQ", 127, length)    msg = token + msg_bytes    conn.send(msg)    return True

二、基于Python实现简单示例

1、基于Python socket实现的WebSocket服务端

# !/usr/bin/env python# -*- coding:utf-8 -*-import socketimport base64import hashlibdef get_headers(data):    """    将请求头格式化成字典    :param data: 请求头    :return: 请求头信息字典    """    header_dict = {}    header_str = data.decode("utf8")    for i in header_str.split("\r\n"):        if str(i).startswith("Sec-WebSocket-Key"):            header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip()    return header_dictdef 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 == 126:        token += struct.pack("!BH", 126, length)    else:        token += struct.pack("!BQ", 127, length)    msg = token + msg_bytes    conn.send(msg)    return Truedef run():    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)    sock.bind(('127.0.0.1', 9527))    sock.listen(5)    conn, address = sock.accept()    data = conn.recv(1024)    headers = get_headers(data)    # 对请求头中的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://127.0.0.1:9527\r\n\r\n"    magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'    value = headers['Sec-WebSocket-Key'] + magic_string    ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())    response_str = response_tpl % (ac.decode('utf-8'))    # 响应【握手】信息    conn.send(response_str.encode("utf-8"))    while True:        try:            info = conn.recv(8096)        except Exception as e:            info = None        if not info:            break        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')        send_msg(conn, body.encode('utf-8'))    sock.close()if __name__ == '__main__':    run()

2、利用JavaScript类库实现客户端

    

更多websocket详解参见:

websocket原理参考博客:

转载于:https://www.cnblogs.com/li-li/p/10305277.html

你可能感兴趣的文章
【LEETCODE】205-Isomorphic Strings
查看>>
【LEETCODE】204-Count Primes
查看>>
【LEETCODE】228-Summary Ranges
查看>>
【LEETCODE】27-Remove Element
查看>>
【LEETCODE】66-Plus One
查看>>
【LEETCODE】26-Remove Duplicates from Sorted Array
查看>>
【LEETCODE】118-Pascal's Triangle
查看>>
【LEETCODE】119-Pascal's Triangle II
查看>>
word2vec 模型思想和代码实现
查看>>
怎样做情感分析
查看>>
用深度神经网络处理NER命名实体识别问题
查看>>
用 RNN 训练语言模型生成文本
查看>>
RNN与机器翻译
查看>>
用 Recursive Neural Networks 得到分析树
查看>>
RNN的高级应用
查看>>
TensorFlow-7-TensorBoard Embedding可视化
查看>>
轻松看懂机器学习十大常用算法
查看>>
一个框架解决几乎所有机器学习问题
查看>>
特征工程怎么做
查看>>
机器学习算法应用中常用技巧-1
查看>>