【问题标题】:Sending structured data over a network [closed]通过网络发送结构化数据 [关闭]
【发布时间】:2014-05-10 07:41:57
【问题描述】:

我是网络编程的初学者,所以,如果我的问题可能显得有点明显,请见谅。

我正在尝试将一些数据从 Qt 应用程序发送到 Python 服务器,该服务器将处理它们并发回一些答案。

允许我在QTcpSocket 类中发送数据的方法是:

// ...
write(const QByteArray &)
write(const char *)
// ...

我的应用程序将管理:身份验证、发送和接收一些复杂的数据,例如struct,以及文件。

我对这种情况有很多疑问:

  1. 上述方法是否足以发送复杂数据?如何发送?
  2. 如何处理服务器端的数据类型(使用Python)?
  3. 您认为我应该使用其他协议,例如 HTTP(带有 QNetworkAccessManager 类)吗?

【问题讨论】:

  • 无论哪种网络方式,都需要看serialisationHere 是一个简短的 python 用例(虽然它不是 Qt)。
  • 是的,通用字节套接字能够传输各种数据(但可能不是最简单的解决方案)。客户端和服务器代码都是您编写的,还是有现有的服务器,您必须适应它的工作方式?
  • @deviantfan 是的,客户端和服务器都是我写的。

标签: python c++ qt qtcore qtnetwork


【解决方案1】:

尝试回答您的问题:

上面提到的方法是否足以发送复杂的数据,以及如何发送?

嗯,是的,发送原始字节数组是最低级别的格式。但是,您需要一个某种东西,它可以让您从复杂数据到字节数组以及从字节数组返回到复杂数据。

这个过程以不同的方式调用,编码、序列化、编组......但总的来说,它只是意味着创建一个系统,将复杂的结构编码为一系列字节或字符

您可以选择很多:ASN.1JSONXMLGoogle's protocol buffersMIME....

您甚至可以设计自己的(例如,一个简单的模式使用 TLV:(Tag-Length-Value),其中 Tag 是 Type 的标识符,Value 可以是基本类型 [并且您必须为您认为是基本的每种类型] 或一个或多个 TLV),长度表示用于对值进行编码的字节数/字符数。

选择什么很大程度上取决于您在哪里编码(语言/平台)和您在哪里解码(语言/平台)以及您对速度、带宽使用、传输、是否应检查消息...等的要求。

如果您正在处理异构架构,您可能需要考虑endianness

最后,您应该区分格式(即复杂结构如何表示为行中的字节序列)和用于编码的库(或用于解码的库)。有时它们会被链接,有时对于相同的格式,您可以选择要使用的库。

如何处理服务器端的数据类型(用Python)?

所以,这里你有一个要求......如果你要使用外部提供的格式,你必须确保它有一个能够解码它的 python 库。

如果您要寻求自主开发的解决方案,您应该定义的一件事是将复杂的 C++ 结构表达为 Python 结构。

另一种可能性是用 C++ 做所有事情,对于 python 服务器端,使用其中一个系统在 C++ 中创建 python 扩展(例如boost-pythonswig....)

您认为我应该使用其他协议,例如 HTTP(带有 QNetworkAccessManager 类)吗?

这取决于您尝试做什么。 有许多广泛可用的 HTTP 库可供您在不同的语言和不同的架构上使用。

您仍然需要解决决定信息格式的问题(尽管 HTTP 有一些定义的做法)。

此外,HTTP 显然偏向于客户端与服务器的通信,其操作始终由客户端发起。

当服务器是需要发起通信的服务器还是需要发送自发信息的服务器时,事情会变得复杂(或不太广泛支持)。

【讨论】:

    【解决方案2】:

    我认为应该区分的不是语言数据结构类型,而是更多关于您发送过来的数据。注意,不同的语言可能有不同的语言结构等等。那只是非常低级的细节。更重要的是您发送的内容。

    您可以查看以下示例,在 QtCore 中序列化/反序列化如何使用 json 格式。 json 模块在 python 中也很好地支持 Json,因此在服务器端反序列化它不会有问题:

    JSON Save Game Example

    这基本上是在客户端给你一些提示的重要部分。不要迷失在保存到文件中。它基本上是将原始字节写入文件,您可以通过网络发送来替换它:

    void Game::write(QJsonObject &json) const
    {
        QJsonObject playerObject;
        mPlayer.write(playerObject);
        json["player"] = playerObject;
    
        QJsonArray levelArray;
        foreach (const Level level, mLevels) {
            QJsonObject levelObject;
            level.write(levelObject);
            levelArray.append(levelObject);
        }
        json["levels"] = levelArray;
    }
    

    ...然后您将在服务器端执行类似的操作,而不是从文件读取,而是从网络读取,但这并不是什么大问题,因为两者都是 IO。

    import json
    json_data=open(file_directory).read()
    
    data = json.loads(json_data)
    pprint(data)
    

    您可以使用原始协议来设计自己的协议,或者只使用扩展。我建议使用一些标准的东西,比如 http (tcp/udp)。然后,您只需要为自己的数据定义 json 格式,而无需处理所有其余的事情,例如单向或双向通信、针对回复攻击的事务标识符、时间戳、数据大小等。

    这将使您真正专注于对您来说重要的事情。一旦您定义了自己的 json 格式,您可以查看 QtNetwork module 以根据需要发送、获取、放置和删除请求。

    您可能会与QNetworkManagerQNetworkReply 等类密切合作。在这里,您可以在 Qt 中使用 QtCore 的 json 找到一个简单的客户端实现,以实现简单的 pastebin 功能:

    #include <QSslError>
    #include <QNetworkAccessManager>
    #include <QNetworkRequest>
    #include <QNetworkReply>
    #include <QTcpSocket>
    
    #include <QJsonDocument>
    #include <QJsonObject>
    #include <QJsonParseError>
    #include <QFile>
    #include <QScopedPointer>
    #include <QTextStream>
    #include <QStringList>
    #include <QCoreApplication>
    
    #include <QDebug>
    
    int main(int argc, char **argv)
    {
        QCoreApplication application{argc, argv};
        application.setOrganizationName(R"("CutePaste")");
        application.setApplicationName(R"("CutePaste Desktop Console Frontend")");
    
        QTextStream standardOutputStream{stdout};
        QFile dataFile;
        QString firstArgument{QCoreApplication::arguments().size() < 2 ? QString() : QCoreApplication::arguments().at(1)};
        if (!firstArgument.isEmpty()) {
            dataFile.setFileName(firstArgument);
            dataFile.open(QIODevice::ReadOnly);
        } else {
            dataFile.open(stdin, QIODevice::ReadOnly);
        }
    
        QByteArray pasteTextByteArray{dataFile.readAll()};
    
        QJsonObject requestJsonObject;
        requestJsonObject.insert(QStringLiteral("data"), QString::fromUtf8(pasteTextByteArray));
        requestJsonObject.insert(QStringLiteral("language"), QStringLiteral("text"));
    
        QJsonDocument requestJsonDocument{requestJsonObject};
    
        QString baseUrlString{QStringLiteral(R"("http://pastebin.kde.org")")};
    
        QNetworkRequest networkRequest;
        networkRequest.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, true);
        networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, R"("application/json")");
        networkRequest.setUrl(QUrl(baseUrlString + R"("/api/json/create")"));
    
        QNetworkAccessManager networkAccessManager;
        QScopedPointer<QNetworkReply> networkReplyScopedPointer(networkAccessManager.post(networkRequest, requestJsonDocument.toJson()));
        QObject::connect(networkReplyScopedPointer.data(), &QNetworkReply::finished, [&] {
    
            QJsonParseError jsonParseError;
            QByteArray replyJsonByteArray{networkReplyScopedPointer->readAll()};
            QJsonDocument replyJsonDocument{QJsonDocument::fromJson(replyJsonByteArray, &jsonParseError)};
            if (jsonParseError.error != QJsonParseError::NoError) {
                qDebug() << R"("The json network reply is not valid json:")" << jsonParseError.errorString();
                QCoreApplication::quit();
            }
    
            if (!replyJsonDocument.isObject()) {
                qDebug() << R"("The json network reply is not an object")";
                QCoreApplication::quit();
            }
    
            QJsonObject replyJsonObject{replyJsonDocument.object()};
            QJsonValue resultValue{replyJsonObject.value(QStringLiteral("result"))};
    
            if (!resultValue.isObject()) {
                qDebug() << R"("The json network reply does not contain an object for the "result" key")";
                QCoreApplication::quit();
            }
    
            QJsonValue identifierValue{resultValue.toObject().value(QStringLiteral("id"))};
    
            if (!identifierValue.isString()) {
                qDebug() << R"("The json network reply does not contain a string for the "id" key")";
                QCoreApplication::quit();
            }
    
            endl(standardOutputStream << baseUrlString << '/' << identifierValue.toString());
    
            QCoreApplication::quit();
        });
    
        QObject::connect(networkReplyScopedPointer.data(), static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), [&](QNetworkReply::NetworkError networkReplyError) {
            if (networkReplyError != QNetworkReply::NoError)
                endl(standardOutputStream << networkReplyScopedPointer->errorString());
        });
    
        QObject::connect(networkReplyScopedPointer.data(), &QNetworkReply::sslErrors, [&](QList<QSslError> networkReplySslErrors) {
            if (!networkReplySslErrors.isEmpty()) {
                for (const auto &networkReplySslError : networkReplySslErrors)
                    endl(standardOutputStream << networkReplySslError.errorString());
            }
        });
    
        int returnValue{application.exec()};
    
        dataFile.close();
        if (dataFile.error() != QFileDevice::NoError)
            endl(standardOutputStream << dataFile.errorString());
    
        return returnValue;
    }
    

    JSON 在这里定义:

    http://sayakb.github.io/sticky-notes/pages/api/

    当然,这不是唯一的方法,例如如果你需要效率,你可以研究一下像capnproto这样的二进制格式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-11
      • 1970-01-01
      • 2014-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多