如何使用 python 套接字模块解码 QTcpServer 发送的 QByteArray?

How do you decode QByteArray sent by QTcpServer using python socket module?

考虑这段代码:

client.py

import socket
import json

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    s.connect(('127.0.0.1', 8000))
    s.send(json.dumps({'text': 'hello world'}).encode("utf-8"))
    data = s.recv(1024)
    s.close()
    print("received data:", data)
except Exception as e:
    print("-->", e)

server.py

import sys
import json

from PyQt5.Qt import *  # noqa


class Server(QDialog):
    def __init__(self):
        super().__init__()
        self.tcp_server = None

    def run(self):
        self.tcp_server = QTcpServer(self)
        if not self.tcp_server.listen(QHostAddress('127.0.0.1'), 8000):
            print("Can't listen!")
            self.close()
            return
        self.tcp_server.newConnection.connect(self.on_new_connection)

    def on_new_connection(self):
        client_connection = self.tcp_server.nextPendingConnection()
        client_connection.waitForReadyRead()
        message_dct = json.loads(
            client_connection.readAll().data().decode("utf-8"))
        print("received json:", message_dct)
        block = QByteArray()
        out = QDataStream(block, QIODevice.ReadWrite)
        out.writeString(json.dumps({'response': len(message_dct["text"])}).encode("utf-8"))
        client_connection.disconnected.connect(client_connection.deleteLater)
        client_connection.write(block)
        client_connection.disconnectFromHost()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    server = Server()
    server.run()
    sys.exit(server.exec_())

在运行宁server.py和client.py之后,client.py将打印:

received data: b'\x00\x00\x00\x11{"response": 11}\x00'

我不明白字典周围的额外字节(最初的4个字节\x00\x00\x00\x11)和最后一个字节(\x00)是什么意思,它们是否代表添加的TCP元数据信息通过 QTcpServer?

无论如何,我想知道在 client.py 上解码字典的正确方法是什么,这样你就会得到字典 {"response": 11}

NS:client.py 旨在 运行 python3.3.6 python 进程,其中最简单的方法是使用标准库(即:使用扭曲或外部 deps 是棘手的),还有...... asyncio 不可用......这就是为什么我使用旧的 socket 模块。

QDataStream是一个class,负责对各种类型的数据进行序列化和实现,所以需要发送额外的信息,比如信息的大小。

正在查看您的消息:

json.dumps({'response': len(message_dct["text"])})

{"response": 11}

这是一个字符串,你用encode("utf-8")将它转换为字节,显示字符0x00表示字符结束:

0x7B 0x22 0x72 0x65 0x73 0x70 0x6F 0x6E 0x73 0x65 0x22 0x3A 0x20 0x31 0x31 0x7D 0x00

所以信息的长度是17字节,十六进制是0x11

QDataStream在发送数据的时候会用4个字节表示数据的大小,所以我们观察前4个字节在数值上是11:

b'\x00\x00\x00\x11{"response": 11}\x00'
[     length     ][       data       ]

对于上面的行为我们已经理解了。

所以有2个解决方案:

  1. 在客户端使用 QDataStream 解码数据,但正如您所指出的,您可以使用的包有限制。

  2. 不使用QDataStream,直接发送字节。

    def on_new_connection(self):
        client_connection = self.tcp_server.nextPendingConnection()
        client_connection.waitForReadyRead()
        message_dct = json.loads(
            client_connection.readAll().data().decode("utf-8"))
        print("received json:", message_dct)
        block = json.dumps({'response': len(message_dct["text"])}).encode("utf-8")
        client_connection.disconnected.connect(client_connection.deleteLater)
        client_connection.write(block)
        client_connection.disconnectFromHost()