0

私はネットワーク プログラミングの初心者なので、私の質問が少し明白に見えるかもしれません。

QtアプリケーションからPythonサーバーにデータを送信しようとしています。Pythonサーバーはそれらを処理して回答を返します。

クラスでデータを送信できるメソッドは次のQTcpSocketとおりです。

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

私のアプリケーションは、認証、 、ファイルなどの複雑なデータの送受信を管理structします。

この状況について多くの質問があります。

  1. 上記の方法は複雑なデータを送信するのに十分ですか?
  2. サーバー側でデータ型を処理する方法 (Python を使用) ?
  3. QNetworkAccessManagerHTTP のような他のプロトコルを (クラスで)使用する必要があると思いますか?
4

2 に答える 2

3

あなたの質問に答えようとしています:

上記の方法は複雑なデータを送信するのに十分ですか?

そうです、未加工のバイト配列を送信するのが最も低いレベルのフォーマットです。ただし、複雑なデータからバイト配列に、バイト配列から複雑なデータに一意に戻すことができるものが必要です。

このプロセスは、エンコード、シリアル化、マーシャリングなど、さまざまな方法で呼び出されます。しかし、一般的には、複雑な構造を一連のバイトまたは文字にエンコードするためのシステムを作成することを意味します。

選択できるものはたくさんあります: ASN.1JSONXMLGoogle のプロトコル バッファ、またはMIME ....

独自に設計することもできます (たとえば、単純なスキーマは TLV: (Tag-Length-Value) を使用しています。ここで、Tag は Type の識別子で、Value は基本型にすることができます [そして、各型の表現を定義する必要があります。基本と見なす] または 1 つ以上の TLV)、長さは、値をエンコードするために使用されるバイト/文字数を示します。

何を選択するかは、エンコードする場所 (言語/プラットフォーム) とデコードする場所 (言語/プラットフォーム) と、速度、帯域幅の使用、トランスポート、メッセージを検査する必要があるかどうかなどの要件に大きく依存します。

異種アーキテクチャを扱っている場合は、エンディアンについて考える必要があるかもしれません。

最後に、フォーマット (つまり、複雑な構造が行内の一連のバイトとして表現される方法) と、エンコードに使用されるライブラリ (またはデコードに使用されるライブラリ) を区別する必要があります。それらはリンクされることもあれば、同じフォーマットに対して使用するライブラリを選択できることもあります。

サーバー側でデータ型を処理する方法 (Python を使用) ?

したがって、ここに要件があります...外部から提供された形式を使用する場合は、それをデコードできるpythonライブラリがあることを確認する必要があります。

自家製のソリューションを使用する場合、定義する必要があるものの 1 つは、複雑な C++ 構造を Python 構造として表現することです。

追加の可能性は、すべてを C++ で行うことです。Python サーバー側では、C++ で Python 拡張機能を作成するためのシステムの 1 つを使用します (例: boost-pythonまたはswig ....) 。

HTTP のような他のプロトコル (QNetworkAccessManager クラスを使用) を使用する必要があると思いますか?

それはあなたが何をしようとしているかによって異なります。さまざまな言語やさまざまなアーキテクチャで使用できる、広く利用可能な HTTP ライブラリが多数あります。

情報のフォーマットを決定するという問題を解決する必要があります (ただし、HTTP にはいくつかの定義済みのプラクティスがあります)。

さらに、HTTP は明らかにクライアントとサーバーの通信に偏っており、アクションは常にクライアントによって開始されます。

サーバーが通信を開始する必要がある場合、または自発的な情報を送信する必要がある場合、事態は複雑になります (またはあまり広くサポートされていません)。

于 2014-05-10T08:47:23.830 に答える
1

区別する必要があるのは言語のデータ構造タイプではないと思いますが、それは送信するデータに関するものです。言語が異なれば、言語構造なども異なる場合があることに注意してください。それは本当に低レベルの詳細です。それよりも重要なのは、あなたが何を送るかです。

QtCore の json 形式でシリアライゼーション/デシリアライゼーションがどのように機能するかを次の例で調べることができます。Json は json モジュールによって python でもサポートされているため、サーバー側で逆シリアル化する問題はありません。

JSON 保存ゲームの例

これは基本的に、クライアント側でいくつかのヒントを与える重要な部分です。ファイルへの保存に迷わないでください。基本的には、生のバイトをファイルに書き込みます。これは、ネットワーク経由で送信することで置き換えられます。

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モジュールを調べて、必要に応じてpost、get、put、およびdeleteリクエストを送信できます。

おそらく、 QNetworkManagerQNetworkReplyクラスなどと密接に連携するでしょう。ここでは、単純なペーストビン機能のための QtCore の json を使用した Qt での単純なクライアント実装を見つけることができます。

#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のようなバイナリ形式を検討することをお勧めします。

于 2014-05-10T08:58:50.817 に答える