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>

 

转载于:https://www.cnblogs.com/wangqj1996/p/9244601.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值