websocket 实现单聊群聊 以及 握手原理+加密方式

WebSocket

  • 开始代码 服务端

群聊

  • # type:WebSocket geu 给变量标注类型
# websocket   web + socket
from geventwebsocket.server import WSGIServer # 我要WSGI为我提供服务
from geventwebsocket.handler import WebSocketHandler # WSGI 遇到 WS协议的时候,处理方式
from geventwebsocket.websocket import WebSocket # 语法提示

# 基于 Flask + geventwebsocket

from flask import Flask,request,render_template

app = Flask(__name__)

user_socket_list = []

@app.route("/ws")
def my_ws_func():
    # print(dir(request.environ))
    user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket
    user_socket_list.append(user_socket)

    print(user_socket)
    while 1:
        msg = user_socket.receive() # 等待接收客户端发送过来的消息
        for us in user_socket_list:
            if us == user_socket:
                continue
            try:
                us.send(msg)
            except:
                continue

        # print(msg)
        # user_socket.send(msg)

@app.route("/group_p")
def group_p():
    return render_template("group_p.html")


if __name__ == '__main__':
    # app.run()
    http_serv = WSGIServer(("0.0.0.0",8181),application=app,handler_class=WebSocketHandler)
    http_serv.serve_forever()
  • 模板代码: group_p.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>发送消息:<input type="text" id="message"><button onclick="send_msg()">发送</button></p>
    <div id="message_list" style="width: 500px">

    </div>
</body>
<script type="text/javascript">
    var ws = new WebSocket("ws://192.168.15.113:9527/ws");
    ws.onmessage = function (event) {
        console.log(event.data);

        var ptag = document.createElement("p");
        ptag.innerText = event.data;

        var divtag = document.getElementById("message_list");
        divtag.appendChild(ptag);
    };

    function send_msg() {
        var msg = document.getElementById("message").value;
        ws.send(msg);
    };

</script>
</html>

实现单聊

服务端:

# websocket
from geventwebsocket.server import WSGIServer # 我要WSGI为我提供服务
from geventwebsocket.handler import WebSocketHandler # WSGI 遇到 WS协议的时候,处理方式
from geventwebsocket.websocket import WebSocket # 语法提示

# 基于 Flask + geventwebsocket

from flask import Flask,request,render_template
import json

app = Flask(__name__)

# user_socket_dict = {"YWB":<geventwebsocket.websocket.WebSocket object at 0x000002699374A730>,
# "JWB":<geventwebsocket.websocket.WebSocket object at 0x000002699374A5F8>}

user_socket_dict = {}

@app.route("/ws/<nickname>")
def my_ws_func(nickname):
    # print(dir(request.environ))
    user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket
    user_socket_dict[nickname] = user_socket
    print(len(user_socket_dict),user_socket_dict)
    # web + socket
    print(user_socket)
    while 1:
        msg = user_socket.receive() # 等待接收客户端发送过来的消息
        print(msg)
        msg = json.loads(msg) # Dict
        """
        {
            to_user:YWB
            from_user:JWB
            message:"你就是一只WB"
        }
        """
        to_user_socket = user_socket_dict.get(msg.get("to_user"))
        msg_json = json.dumps(msg)
        to_user_socket.send(msg_json)

@app.route("/one_p")
def one_p():
    return render_template("one_p.html")


if __name__ == '__main__':
    # app.run()
    http_serv = WSGIServer(("0.0.0.0",9527),application=app,handler_class=WebSocketHandler)
    http_serv.serve_forever()

前端Html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>登录<input type="text" id="nick"><button onclick="login()">登录</button></p>
    <p>给<input type="text" id="to_user"></p>
    <p>发送消息:<input type="text" id="message"><button onclick="send_msg()">发送</button></p>
    <div id="message_list" style="width: 500px">

    </div>
</body>
<script type="text/javascript">
    var ws = null;

    function send_msg() {
        var msg = document.getElementById("message").value;
        var to_user = document.getElementById("to_user").value;
        var nick = document.getElementById("nick").value;
        var msg_obj = {
            to_user:to_user,
            from_user:nick,
            msg:msg
        };
        var msg_json = JSON.stringify(msg_obj);
        ws.send(msg_json);
    };

    function login() {
        var nick = document.getElementById("nick").value;
        ws = new WebSocket("ws://192.168.15.113:9527/ws/"+nick);
        ws.onmessage = function (event) {
            console.log(event.data);
            data_obj = JSON.parse(event.data);
            var ptag = document.createElement("p");
            ptag.innerText = data_obj.from_user + " : " + data_obj.msg;
            var divtag = document.getElementById("message_list");
            divtag.appendChild(ptag);
        };
    }

</script>
</html>

websocket 握手原理

WebSocket 加密解密原理
01111111
10001011
00001011

01111111  == 127
11111111  == 255 
01111111  == 127

00000000
11111111  == 255
11111111 00000011

^:
00000000
11111111
11111111

10101010
01010101
11111111

10101010
10000010
00101000

websocket 握手原理+加密方式

  • value = headers['Sec-WebSocket-Key'] + magic_string
  • base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
import socket, base64, hashlib

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)
# 获取客户端socket对象
conn, address = sock.accept()
# 获取客户端的【握手】信息
data = conn.recv(1024)
print(data)
# 原始的 http 协议
"""
b'GET / HTTP/1.1\r\n
Host: 127.0.0.1:9527\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: #\xef\xbf\xa5%\xe2\x80\xa6\xe2\x80\xa6&*\xef\xbc\x88#E$RTY$%RTY%^Y&U%^&*=14241524-85b8-4ccc-a20f-c5ae68e7a9b6\r\n\r\n
"""

# websocket 协议
"""
b'GET / HTTP/1.1\r\n
Host: 127.0.0.1:9527\r\n
Connection: Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
Upgrade: websocket\r\n
Origin: http://localhost:63342\r\n
Sec-WebSocket-Version: 13\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: #\xef\xbf\xa5%\xe2\x80\xa6\xe2\x80\xa6&*\xef\xbc\x88#E$RTY$%RTY%^Y&U%^&*=14241524-85b8-4ccc-a20f-c5ae68e7a9b6\r\n
Sec-WebSocket-Key: 6wD9eV6CaTkUveZOuKi/kg==\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n'
"""

# magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
def get_headers(data):
    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_dict

# def get_header(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

headers = get_headers(data)  # 提取请求头信息
# 对请求头中的sec-websocket-key进行加密
# M/EoWjDzXy+RqE798wx4WA==
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

value = headers['Sec-WebSocket-Key'] + magic_string

print(value) # M/EoWjDzXy+RqE798wx4WA==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
print(ac) # dp5EfotV/BKd8c58hFUJH3Pc0pk=
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"

response_str = response_tpl % (ac.decode('utf-8'))
# 响应【握手】信息
conn.send(response_str.encode("utf8"))
#
while True:
    msg = conn.recv(8096)
    print("服务器收到",msg)

加密

import struct
msg_bytes = "先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。".encode("utf8")
token = b"\x81"
length = len(msg_bytes)

if length <= 254:
    token += struct.pack("B", length)
elif length <= 65535:
    token += struct.pack("!BH", 126, length)
else:
    token += struct.pack("!BQ", 127, length)

msg = token + msg_bytes

print(msg)

解密


hashstr = b'\x81\x95w\xc3z\x8c\x93x\xf0i\xd3j\x9f(\xde%\xca\x18\x92}\xf2h\xcfN\x93\x18\xee'

# hashstr = b'\x81\xfe\x02\xdc]\x9e~:\xb8\x1b\xf6\xdf\xe5\x03\x9b\xb2\xc6z\xc6\xa0\xbb\x02\xd4\xdf\xd0\x14\x96\xba\xd1z\xc6\x97\xb4\x1f\xed\xdf\xe97\x98\x94\xdfq\xc2\xb6\xb9%\xf4\xdf\xf97\x9a\x82\xd6z\xc6\xb3\xb8\x16\xf8\xd5\xe1\x12\x99\xa1\xd7{\xc9\xa4\xba\x08\xcc\xdf\xe1\x14\x91\x86\xd1x\xd3\x9e\xb51\xe4\xdf\xd0/\x98\xba\xf8{\xd3\xa2\xb9$\xdf\xde\xe4\x15\x99\x9d\xd6z\xc7\xa5\xbe\x1e\xfc\xdd\xd9(\x9a\x84\xd0{\xf3\x91\xb9\'\xf5\xd2\xda=\x9a\x82\xd0x\xf9\xb2\xb9$\xf0\xdf\xdb\x1b\x91\x86\xd1{\xc1\x9a\xb8!\xe9\xde\xe4\x15\x9b\x99\xf6{\xc1\xa2\xb5$\xd5\xde\xe7\x10\x9b\x9e\xcbv\xfe\xbf\xb2"\xf2\xdd\xc6\x08\x96\x85\xe0{\xfb\xb2\xb8&\xe3\xde\xe4\x15\x98\x94\xd7w\xff\xbd\xb2"\xf2\xdc\xf1,\x98\xb0\xf8z\xc7\xb1\xb9$\xf0\xd3\xc4\x05\x9a\x82\xd6z\xc7\xa5\xbe\x1e\xfc\xd2\xf2\x04\x9b\x94\xc1{\xc2\xba\xb8"\xde\xdf\xc1=\x9b\xaa\xf1q\xc2\xb6\xb9%\xdb\xdf\xd8\x17\x9b\xbf\xd5{\xc6\xa7\xb4\x1f\xe9\xdf\xe3)\x91\x86\xd1x\xff\x98\xb8"\xe6\xdf\xe2\t\x9b\x99\xf6z\xc7\xb1\xbb.\xea\xd5\xe1\x12\x9a\x82\xd0{\xd0\xa6\xb88\xfa\xd2\xda4\x96\xb5\xefv\xe8\xbe\xb2"\xf2\xdf\xe1\x0b\x9b\xac\xe6{\xda\x8b\xb9\'\xf7\xd5\xe1\x12\x9a\x81\xf8{\xdf\xa4\xb8!\xde\xd2\xed\x11\x9a\x83\xd6v\xc9\x95\xb9\'\xe1\xd9\xdd\x1c\x9b\x94\xf6z\xc6\x97\xb8$\xe2\xde\xe53\x91\x86\xd1z\xc1\x8b\xb9&\xc4\xde\xe5\x1e\x9a\x87\xceq\xc2\xb6\xb4\x07\xe1\xdd\xe0\x04\x96\xbd\xfa{\xee\x9c\xb2"\xf2\xde\xe5\x13\x9b\x94\xc1{\xc2\xb8\xb8\x0e\xf2\xd9\xdd\x1c\x96\xb1\xf8x\xe2\xb3\xb9#\xe2\xdf\xf8&\x99\xb0\xf2y\xd9\xab\xb8\x11\xf4\xde\xe5$\x9b\x85\xfd{\xe8\xbe\xb5\x1e\xfb\xd5\xe1\x12\x9b\x94\xc1z\xc5\xa2\xbb\x02\xf7\xdf\xd2&\x96\x94\xe7{\xfb\x8c\xb8\x16\xef\xd2\xe8\x11\x91\x86\xd1z\xc5\x9f\xbb\x06\xd3\xd3\xc4\x05\x9a\x82\xd6{\xc7\x89\xbb\x06\xf0\xde\xe4\x15\x99\xaa\xdbq\xc2\xb6\xb9&\xf3\xdf\xf3\x02\x9b\xbb\xd2y\xd9\xbb\xb2"\xf2\xde\xe0!\x9b\xbc\xd8{\xda\xac\xb8"\xfc\xdc\xee\x0b\x9a\x83\xc2}\xfe\xb8\xb9 \xf3\xde\xe53\x9d\xba\xdcz\xc0\xb7\xb4\x1d\xf0\xd3\xde3\x98\xae\xe5z\xc7\xb1\xbe\x1e\xff\xd2\xe9\'\x99\x9f\xd3}\xfe\xbb\xb5\x0f\xdd\xdf\xd8\x1f\x99\x97\xd4q\xc2\xb6\xbb3\xda\xdd\xc7\x18\x96\xb3\xf2{\xd0\xa4\xb2"\xf2\xdf\xe2\t\x96\xa3\xcc{\xc1\x9a\xba$\xd1\xd5\xe1\x12\x98\xa2\xf2z\xc5\x9f\xb8\x1b\xf6\xdf\xe5\x03\x99\x94\xddx\xf5\xae\xb9%\xdb\xd3\xdc\t\x97\xa3\xc6z\xc6\xb1\xbe\x1e\xfc\xdc\xd9\x04\x9a\x81\xf8z\xc6\x80\xb80\xd5\xde\xe53\x9a\x83\xd6z\xc4\xb1\xb2"\xf2\xde\xe7\x15\x98\xad\xfd{\xda\x9d\xb8.\xf1\xd5\xe1\x12\x98\xb8\xd4z\xc5\x9f\xb8\x0c\xd6\xde\xe4\x15\x91\x86\xd1y\xfa\x8c\xb8\x0e\xf0\xdc\xcb#\x96\x9b\xd1q\xc2\xb6\xb8!\xfb\xd2\xde#\x96\x99\xf5v\xdf\x9f\xb4\x06\xe7\xdc\xe1\x11\x91\x86\xd1x\xe2\xb3\xbb\x17\xfe\xdf\xe4!\x99\xa1\xd7}\xfe\xb8'

# b'\x81\x8b\xa4\xc4"\xcd\xcc\xa1N\xa1\xcb\xb7Q\xbe\xd7\xb7Q'
# \x8b == 127
# \x8b == 126
# \x8b <= 125
# 将第二个字节也就是 \x83 第9-16位 进行与127进行位运算
print(hashstr[1])
payload = hashstr[1] & 127
print(payload)
# hashstr = b'\x81\xfe\x02\xdc]\x9e~:\xb8\x1b\xf6\xdf\xe5\x03\x9b\xb2\xc6z\xc6\xa0\xbb\x02\xd4\xdf\xd0\x14\x96\xba'
if payload == 127:
    extend_payload_len = hashstr[2:10]
    mask = hashstr[10:14]
    decoded = hashstr[14:]
# 当位运算结果等于127时,则第3-10个字节为数据长度 # \x02\xdc]\x9e~:\xb8\x1b\xf6\xdf\xe5
# 第11-14字节为mask 解密所需字符串 # \x03\x9b\xb2\xc6z
# 则数据为第15字节至结尾

if payload == 126:
    extend_payload_len = hashstr[2:4]
    mask = hashstr[4:8]
    decoded = hashstr[8:]


# print(hashstr[1])
# payload = hashstr[1] & 127
# print(payload)
# 当位运算结果等于126时,则第3-4个字节为数据长度 # 65535
# 第5-8字节为mask 解密所需字符串 # \x9e~:\xb8\x1b\xf6
# 则数据为第9字节至结尾

if payload <= 125:
    extend_payload_len = None
    mask = hashstr[2:6]
    decoded = hashstr[6:]

# b'\x81\x8b\xa4\xc4"\xcd\xcc\xa1N\xa1\xcb\xb7Q\xbe\xd7\xb7Q'

# 当位运算结果小于等于125时,则这个数字就是数据的长度 # \x8b
# 第3-6字节为mask 解密所需字符串 # \xa4\xc4"\xcd\xcc
# 则数据为第7字节至结尾 # \xa1N\xa1\xcb\xb7Q\xbe\xd7\xb7Q
#
str_byte = bytearray()

for i in range(len(decoded)):
    # 0 = \xa1N ^ \xa4
    # 1 = \xa1 ^ \xc4  1 % 4
    # 2 = \xcb ^ \xcd  2 % 4 mask = [0-3]
    byte = decoded[i] ^ mask[i % 4]
    str_byte.append(byte)

print(str_byte.decode("utf8"))

猜你喜欢

转载自www.cnblogs.com/zhang-zi-yi/p/10755724.html