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'\x81\x84\xa3l\xcf\x10\x92^\xfc$'

    客戶端發送到server的websocket的報文分為四個部分:

      a、固定部分‘\81’

      b、報文內容長度

      c、掩碼  b'\xa3l\xcf\x10'

      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\r\n" \
      "Upgrade:websocket\r\n" \
      "Connection: Upgrade\r\n" \
      "Sec-WebSocket-Accept: {1}\r\n" \
      "WebSocket-Location: ws://{2}/chat\r\n" \
      "WebSocket-Protocol:chat\r\n\r\n"


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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM