zoukankan      html  css  js  c++  java
  • 119 websocket 群聊 单聊 websocket的握手 加密解密

    主要内容: https://www.cnblogs.com/nnngu/p/9347635.html

     1 使用websocket实现一个简单的收发机制

    from geventwebsocket.handler import WebSocketHandler
    from geventwebsocket.websocket import WebSocket
    from gevent.pywsgi import WSGIServer
    from flask import Flask, request, render_template
    
    app = Flask(__name__)
    @app.route("/ws")
    def ws():
        user_socket = request.environ.get("wsgi.websocket")
        # print(user_socket)
        while 1:
            msg = user_socket.receive()
            print(msg)
            try:
                user_socket.send(msg)
            except:
                return
    if __name__ == '__main__':
        http_serv = WSGIServer(("0.0.0.0",5000), app, handler_class=WebSocketHandler)
        http_serv.serve_forever()
    

      前端代码:

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta http-equiv="content-Type" charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    </head>
    <body>
    </body>
    <script type="application/javascript">
        // <!--新建一个websocket的连接-->
        var ws = new WebSocket("ws://127.0.0.1:5000/ws");
        //当打开的时候加载
        // ws.onopen = function(){
        //     alert("hello")
        // };
        接收后端穿过来的信息
        ws.onmessage = function (ws_status) {
            console.log(ws_status.data)
        };
    
    </script>
    </html>
    

    2 使用websocket实现群聊

      后端代码:

    from geventwebsocket.handler import WebSocketHandler
    from geventwebsocket.websocket import WebSocket
    from gevent.pywsgi import WSGIServer
    from flask import Flask, request, render_template
    
    app = Flask(__name__)
    user_socket_list = []  # type:list
    
    
    @app.route("/ws")
    def ws():
        user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket
        if user_socket:
            user_socket_list.append(user_socket)
        while 1:
            msg = user_socket.receive()
            print(msg)
            for usocket in user_socket_list:
                if user_socket == usocket:
                    continue
                try:
                    usocket.send(msg)
                except:
                    continue
    
    
    @app.route('/')
    def index():
        return render_template("ws群聊.html")
    
    
    if __name__ == '__main__':
        http_serv = WSGIServer(("0.0.0.0", 5000), app, handler_class=WebSocketHandler)
        http_serv.serve_forever()
    View Code

      前端代码:

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta http-equiv="content-Type" charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    </head>
    <body background="/static/1.jpg">
    
    <p>发送的内容: <input type="text" id="message"> <button onclick="send_msg()">发送信息</button></p>
    <div id="msg_list">
    {#    <img src="/static/1.jpg" alt="" style="height: 400px; height: 400px">#}
    </div>
    </body>
    <script type="application/javascript">
        // <!--新建一个websocket的连接-->
        var ws = new WebSocket("ws://192.168.12.66:5000/ws");
        //当打开的时候加载
        // ws.onopen = function(){
        //     alert("hello")
        // };
        {#接收服务器端传过来的信息#}
        ws.onmessage = function (ws_status) {
            console.log(ws_status.data);
            {#创建一个标签#}
            var ptag = document.createElement("p");
            {#填充里面的内容#}
            ptag.innerText = ws_status.data;
            {#把内容加入到信息列表中#}
            document.getElementById("msg_list").appendChild(ptag)
        };
        function send_msg() {
            var msg = document.getElementById("message").value;
            var ptag = document.createElement("p");
            ptag.style.cssText = "text-align: right";
            ptag.innerText = msg;
            document.getElementById("msg_list").appendChild(ptag);
            ws.send(msg)
        }
    </script>
    </html>
    View Code

    3 使用websocket实现单聊

    4 webocket的握手信息

      a : websocket协议包含连个部分: 一部分是握手, 一部分是数据传输

      b : 服务端的消息如何发送个客户端:

        long long ago~ 服务端想主动的push消息给客户端(比如聊天室的实时收发),这是不可能的,但是,我们可以通过ajax轮询和long poll技术

    制造一个服务端给客户端主动push消息的假象

        ajax轮询:: 让浏览器隔几秒就发送一次请求, 询问服务器是否有新的消息.

            缺点:大大的增加了服务端的负载, 且速度很慢

        long poll :: long poll和ajax的原理都差不多, 都是采用轮询的原理, 只不过来那个poll是采取阻塞的方式去轮询, 即客户端发起一个请求连接,这个连接会阻塞住, 知道服务端有了消息, 才会respnse给客户端.

            缺点: 虽然降低了服务端的负载能力, 但是需要服务端有很高的并发能力.

        websocket解决了服务器与客户端的全双工通信的问题. 解决了服务器的被动性, 当服务端升级后, 服务端可以主动推送信息给客户端.

      c : websocket的握手

        原理: http服务器识别websocket的方式首先是判断http头中的connection和upgrade头, 如果connectin是uprgrade, 且upgrade头是websocket, 则可以确定是webscket请求,这时候要进行websocket的握手处理, 通过Sec-WebSocket-Key的设置进行一些计算返回Sec-WebSocket-Accept的响应头. Sec-WebSocket-Key是客户端随机生成并进行base64的字符串,他的原始内容服务器并不关心, 服务器需要这个字符串与”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″这个字符串进行拼接, 然后对着个拼接好的字符串进行sha1运算, 再进行base64编码, 为响应头Sec-WebSocket-Accept的值

        Sec-WebSocket-Key的作用: 客户端将这个key发给服务器, 服务器对这个key进行处理返回给客户端, 客户端根据这个key是否正确来判断是否建立连接.

        代码:

    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)
    """
    b'GET / HTTP/1.1
    
    Host: 127.0.0.1:9527
    
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0
    
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    
    Accept-Encoding: gzip, deflate
    
    Sec-WebSocket-Version: 13
    
    Origin: http://localhost:63342
    
    Sec-WebSocket-Extensions: permessage-deflate
    
    Sec-WebSocket-Key: 3vbd1/UIZdjSZJ+LmF9+Wg==
    
    Connection: keep-alive, Upgrade
    
    Cookie: csrftoken=XHlcBVZmyRl833qU4WZ0YGPI9QFQsTi0L1TlOrz6cwDgM8EJeHyY2pxhUyg4RNuD
    
    Pragma: no-cache
    
    Cache-Control: no-cache
    
    Upgrade: websocket
    
    '
    """
    #
    # # magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
    magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
    #
    #
    def get_headers(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  # {Sec-WebSocket-Key : 3vbd1/UIZdjSZJ+LmF9+Wg==}
    headers = get_headers(data)  # 提取请求头信息
    # 对请求头中的sec-websocket-key进行加密
    response_tpl = "HTTP/1.1 101 Switching Protocols
    " 
                   "Upgrade:websocket
    " 
                   "Connection: Upgrade
    " 
                   "Sec-WebSocket-Accept: %s
    " 
                   "WebSocket-Location: ws://127.0.0.1:9527
    
    "
    
    value = headers['Sec-WebSocket-Key'] + magic_string
    #  value = 3vbd1/UIZdjSZJ+LmF9+Wg==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
    # print(value)
    ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
    response_str = response_tpl % (ac.decode('utf-8'))
    # 响应【握手】信息
    conn.send(response_str.encode("utf8"))
    #
    while True:
        msg = conn.recv(8096)
        print(msg)
    View Code

        html中的代码:

    <body>
    </body>
    <script type="application/javascript">
        var ws = new WebSocket("ws://127.0.0.1:9527")
    </script>
    </html>
    

      d: 加密

    hashstr = b'x81x89ERx99vxa1xef9x93xe0xef|xe3xcf'
    # 将第二个字节也就是 x8er  进行与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个字节为数据长度
    # 第5-8字节为mask 解密所需字符串
    # 则数据为第9字节至结尾
    if payload <= 125:
        extend_payload_len = None
        mask = hashstr[2:6]  # xf0x89xaex06
        decoded = hashstr[6:]  # x95xe5xc2x01xd0xfexc1x1cx9cxed
    
    # 当位运算结果小于等于125时,则这个数字就是数据的长度
    # 第3-6字节为mask 解密所需字符串
    # 则数据为第7字节至结尾
    str_byte = bytearray()#[b"",b"",b""]
    
    for i in range(len(decoded)):
        byte = decoded[i] ^ mask[i % 4]
        str_byte.append(byte)
    
    print(str_byte.decode("utf8"))
    View Code

      e : 解密:

    import struct
    msg_bytes = "hello".encode("utf8")
    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)
    View Code

        

  • 相关阅读:
    百度地图根据经纬度计算瓦片行列号
    【完全开源】百度地图Web service API C#.NET版,带地图显示控件、导航控件、POI查找控件
    TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端
    重中之重:委托与事件
    可复用代码:组件的来龙去脉
    [史上最全]C#(VB.NET)中位运算符工作过程剖析(译)
    物以类聚:对象也有生命
    导入导出Android手机文件
    Android修改system只读权限
    SSHDroid(SSH Server for Android)通过PC或命令连接android
  • 原文地址:https://www.cnblogs.com/gyh412724/p/10156175.html
Copyright © 2011-2022 走看看