• python3 实现 websocket server 解决中文乱码


    一、什么是websocket

    WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

    在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

    浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

    当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。      -----------------    出自菜鸟教程

    二、客户端请求报文

    客户端请求的链接:ws://localhost:8080

    和传统http报文不同的地方:

      Connection: Upgrade

      Upgrade: websocket    -----   表示发起的是websocket协议

      Sec-WebSocket-Key: TD7emWUct4iW4vddYWbMqQ==   ------   由浏览器随机生成,提供基本的防护

      Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits    ----    协议的扩展

      Sec-WebSocket-Version: 13   ----   版本号

    三、服务器接收请求报文

    服务器收到请求报文后,会发起tcp的三次握手,和客户端建立链接,这个地方和tcpsocket基本一样。

    # 创建基于tcp的服务器
    serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    host = (HOST, PORT)
    serverSocket.bind(host)
    serverSocket.listen(128)
    print("服务器运行, 等待用户链接")
    while True:
        # print("getting connection")
        clientSocket, addressInfo = serverSocket.accept()
        # print("get connected")
        request = clientSocket.recv(2048)
        print(request.decode())
        # 获取Sec-WebSocket-Key
        ret = re.search(r"Sec-WebSocket-Key: (.*==)", str(request.decode()))
        if ret:
            key = ret.group(1)
        else:
            return
        Sec_WebSocket_Key = key + MAGIC_STRING
        # print("key ", Sec_WebSocket_Key)
        # 将Sec-WebSocket-Key先进行sha1加密,转成二进制后在使用base64加密
        response_key = base64.b64encode(hashlib.sha1(bytes(Sec_WebSocket_Key, encoding="utf8")).digest())
        response_key_str = str(response_key)
        response_key_str = response_key_str[2:30]
        # print(response_key_str)
        # 构建websocket返回数据
        response = HANDSHAKE_STRING.replace("{1}", response_key_str).replace("{2}", HOST + ":" + str(PORT))
        clientSocket.send(response.encode())
        # print("send the hand shake data")

     四、因为websocket是基于tcp的全双工通信协议,所以,他可以一边接收,一边发送

      1、接收并解析websocket报文

        b'x81x84xa3lxcfx10x92^xfc$'

        客户端发送到server的websocket的报文分为四个部分:

          a、固定部分‘81’

          b、报文内容长度

          c、掩码  b'xa3lxcfx10'

          d、报文内容   b'x92^xfc$'

    def recv_data(clientSocket):
        try:
            info = clientSocket.recv(2048)
            if not info:
                return
        except:
            return 
        else:
            code_len = info[1] & 0x7f
            if code_len == 0x7e:
                extend_payload_len = info[2:4]
                mask = info[4:8]
                decoded = info[8:]
            elif code_len == 0x7f:
                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)
            raw_str = str(bytes_list, encoding="utf-8")
            print(raw_str)

      2、server端发送数据

        server端发送数据分为三个部分

          a、固定部分‘81’

          b、报文长度

          c、报文内容

        struct用法:

          

    Format    C Type    Python type    Standard size    Notes
    x    pad byte    no value          
    c    char    bytes of length 1    1     
    b    signed char    integer    1    (1),(3)
    B    unsigned char    integer    1    (3)
    ?    _Bool    bool    1    (1)
    h    short    integer    2    (3)
    H    unsigned short    integer    2    (3)
    i    int    integer    4    (3)
    I    unsigned int    integer    4    (3)
    l    long    integer    4    (3)
    L    unsigned long    integer    4    (3)
    q    long long    integer    8    (2), (3)
    Q    unsigned long long    integer    8    (2), (3)
    n    ssize_t    integer         (4)
    N    size_t    integer         (4)
    e    (7)    float    2    (5)
    f    float    float    4    (5)
    d    double    float    8    (5)
    s    char[]    bytes          
    p    char[]    bytes          
    P    void *    integer         (6)
    Character    Byte order    Size    Alignment
    @    native    native    native
    =    native    standard    none
    <    little-endian    standard    none
    >    big-endian    standard    none
    !    network (= big-endian)    standard    none

    服务端发送数据代码:

    def send_data(clientSocket):
        data = "need to send messages中文"
        token = b'x81'
        length = len(data.encode())
        if length<=125:
            token += struct.pack('B', length)
        elif length <= 0xFFFF:
            token += struct.pack('!BH', 126, length)
        else:
            token += struct.pack('!BQ', 127, length)
        data = token + data.encode()
        clientSocket.send(data)

    全部代码:

    py:

    import socket
    import base64
    import hashlib
    import re
    import threading
    import struct
    
    
    HOST = "localhost"
    PORT = 8080
    MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
    HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols
    " 
          "Upgrade:websocket
    " 
          "Connection: Upgrade
    " 
          "Sec-WebSocket-Accept: {1}
    " 
          "WebSocket-Location: ws://{2}/chat
    " 
          "WebSocket-Protocol:chat
    
    "
    
    
    def recv_data(clientSocket):
        try:
            info = clientSocket.recv(2048)
            if not info:
                return
        except:
            return 
        else:
            print(info)
            code_len = info[1] & 0x7f
            if code_len == 0x7e:
                extend_payload_len = info[2:4]
                mask = info[4:8]
                decoded = info[8:]
            elif code_len == 0x7f:
                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()
            print(mask)
            print(decoded)
            for i in range(len(decoded)):
                chunk = decoded[i] ^ mask[i % 4]
                bytes_list.append(chunk)
            raw_str = str(bytes_list, encoding="utf-8")
            print(raw_str)
    
    
    def send_data(clientSocket):
        data = "need to send messages中文"
        token = b'x81'
        length = len(data.encode())
        if length<=125:
            token += struct.pack('B', length)
        elif length <= 0xFFFF:
            token += struct.pack('!BH', 126, length)
        else:
            token += struct.pack('!BQ', 127, length)
        data = token + data.encode()
        clientSocket.send(data)
    
    
    
    def handshake(serverSocket):
        while True:
            # print("getting connection")
            clientSocket, addressInfo = serverSocket.accept()
            # print("get connected")
            request = clientSocket.recv(2048)
            print(request.decode())
            # 获取Sec-WebSocket-Key
            ret = re.search(r"Sec-WebSocket-Key: (.*==)", str(request.decode()))
            if ret:
                key = ret.group(1)
            else:
                return
            Sec_WebSocket_Key = key + MAGIC_STRING
            # print("key ", Sec_WebSocket_Key)
            # 将Sec-WebSocket-Key先进行sha1加密,转成二进制后在使用base64加密
            response_key = base64.b64encode(hashlib.sha1(bytes(Sec_WebSocket_Key, encoding="utf8")).digest())
            response_key_str = str(response_key)
            response_key_str = response_key_str[2:30]
            # print(response_key_str)
            # 构建websocket返回数据
            response = HANDSHAKE_STRING.replace("{1}", response_key_str).replace("{2}", HOST + ":" + str(PORT))
            clientSocket.send(response.encode())
            # print("send the hand shake data")
            t1 = threading.Thread(target = recv_data, args = (clientSocket,))
            t1.start()
            t2 = threading.Thread(target = send_data, args = (clientSocket,))
            t2.start()
    
    
    def main():
        # 创建基于tcp的服务器
        serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        host = (HOST, PORT)
        serverSocket.bind(host)
        serverSocket.listen(128)
        print("服务器运行, 等待用户链接")
        # 调用监听
        handshake(serverSocket)
    
    
    if __name__ == "__main__":
        main()

    html:

    <!DOCTYPE html>
    <html>
    <head>
    <title>w</title>
    <html>
    <head lang="en">
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <div>
            <input type="text" id="txt"/>
            <input type="button" id="btn" value="提交" onclick="sendMsg();"/>
            <input type="button" id="close" value="关闭连接" onclick="closeConn();"/>
        </div>
        <div id="content"></div>
     
    <script type="text/javascript">
        var socket = new WebSocket("ws://127.0.0.1:8080");
     
        socket.onopen = function () {
            /* 与服务器端连接成功后,自动执行 */
     
            var newTag = document.createElement('div');
            newTag.innerHTML = "【连接成功】";
            document.getElementById('content').appendChild(newTag);
        };
     
        socket.onmessage = function (event) {
            /* 服务器端向客户端发送数据时,自动执行 */
            var response = event.data;
            var newTag = document.createElement('div');
            newTag.innerHTML = response;
            document.getElementById('content').appendChild(newTag);
        };
     
        socket.onclose = function (event) {
            /* 服务器端主动断开连接时,自动执行 */
            var newTag = document.createElement('div');
            newTag.innerHTML = "【关闭连接】";
            document.getElementById('content').appendChild(newTag);
        };
     
        function sendMsg() {
            var txt = document.getElementById('txt');
            socket.send(txt.value);
            txt.value = "";
        }
        function closeConn() {
            socket.close();
            var newTag = document.createElement('div');
            newTag.innerHTML = "【关闭连接】";
            document.getElementById('content').appendChild(newTag);
        }
     
    </script>
    </body>
    </html>
  • 相关阅读:
    [转]虚拟机下Redhat Linux系统的Mplayer安装实现
    结构定义中元素位置排列问题
    RPM 的介绍和应用
    yum 升级 rhe15
    Microsoft Access Data Types
    Eclipse配置SVN
    搭建本地YUM软件仓库
    2011年度最佳开源软件:Bossie奖结果公布
    Linux中文man在线手册
    PID算法
  • 原文地址:https://www.cnblogs.com/wangqj1996/p/9244601.html
Copyright © 2020-2023  润新知