通过网络发送结构化数据

时间:2014-05-10 07:41:57

标签: python c++ qt qtcore qtnetwork

我是网络编程的佼佼者,所以,对不起,如果我的问题看起来有点明显。

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

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

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

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

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

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

2 个答案:

答案 0 :(得分:3)

试着回答你的问题:

  

上述方法是否足以发送复杂数据,以及如何发送?

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

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

您可以选择许多内容: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显然偏向于客户端与服务器的通信,操作始终由客户端启动。

如果服务器是需要发起通信的服务器或需要发送自发信息的服务器,那么事情变得复杂(或者支持得不太广泛)。

答案 1 :(得分:1)

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

您可以查看以下示例,序列化/反序列化如何在QtCore中使用json格式。 json模块也很好地支持Json中的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这样的二进制格式。