我是网络编程的佼佼者,所以,对不起,如果我的问题看起来有点明显。
我正在尝试将一些数据从Qt应用程序发送到Python服务器,该服务器将处理它们并发回一些答案。
允许我在QTcpSocket
类中发送数据的方法是:
// ...
write(const QByteArray &)
write(const char *)
// ...
我的应用程序将管理:验证,发送和接收一些复杂的数据,如struct
和文件。
我对这种情况有很多疑问:
QNetworkAccessManager
类)吗?答案 0 :(得分:3)
试着回答你的问题:
上述方法是否足以发送复杂数据,以及如何发送?
嗯,是的,发送原始字节数组是最低级别的格式。但是,您需要一个 something ,它可以使您从复杂数据到字节数组以及从字节数组返回复杂数据。
这个过程以不同的方式调用,编码,序列化,编组....但一般来说,它只是意味着创建一个系统,用于将复杂结构编码为字节或字符序列
您可以选择许多内容:ASN.1,JSON,XML,Google's protocol buffers或MIME ....
你甚至可以设计自己的(例如一个简单的模式使用TLV :( Tag-Length-Value),其中Tag是Type的标识符,Value可以是基本类型[并且你必须定义一个表示形式]你认为基本的每种类型]或者一个或多个TLV),长度表示用于编码值的字节/字符数。
选择什么取决于您编码的位置(语言/平台)和解码位置(语言/平台)以及您对速度,带宽使用,传输,是否应检查消息等的要求等等。
如果您正在处理异构架构,则可能需要考虑endianness。
最后,您应该区分格式(即复杂结构如何表示为行中的字节序列)和用于编码的库(或用于解码的库)。有时它们会被链接,有时以相同的格式,你可以选择使用它们。
如何处理服务器端的数据类型(使用Python)?
所以,这里有一个要求......如果你想要一个外部提供的格式,你必须确保它有一个能够解码它的python库。
如果您要使用自行开发的解决方案,那么您应该定义的一件事就是将复杂的C ++结构表达为Python结构。
另一种可能性是用C ++完成所有事情,并且python服务器端使用其中一个系统在C ++中创建python扩展(例如boost-python或swig ....)
您认为我应该使用HTTP等其他协议(使用QNetworkAccessManager类)吗?
这取决于你尝试做什么。 有许多可用于不同语言和不同体系结构的HTTP库。
您仍然需要解决决定信息格式的问题(尽管HTTP有一些已定义的做法)。
此外,HTTP显然偏向于客户端与服务器的通信,操作始终由客户端启动。
如果服务器是需要发起通信的服务器或需要发送自发信息的服务器,那么事情变得复杂(或者支持得不太广泛)。
答案 1 :(得分:1)
我认为不应该区分语言数据结构类型,但它更多地是关于您发送的数据。请注意,不同的语言可能具有不同的语言结构等。这只是非常低级的细节。更重要的是你发送的内容。
您可以查看以下示例,序列化/反序列化如何在QtCore中使用json格式。 json模块也很好地支持Json中的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 module发送帖子,获取,发送和删除请求。
您可能会与QNetworkManager,QNetworkReply类密切合作,等等。在这里,您可以在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这样的二进制格式。