C++ QT NetworkManager初始化和异步问题
首先,我想在创建这个类时触发一个初始化过程。(DNS查找已知主机,并通过指定数量的插槽与主机进行预握手-测试后不得硬编码)-在创建所有类、读取配置等过程中 其次,我希望根据特定主机允许(和打开)的插槽允许并行请求发送 坦率地说,下面的代码存在多个问题C++ QT NetworkManager初始化和异步问题,c++,qt,qnetworkaccessmanager,qnetworkrequest,qnetworkreply,C++,Qt,Qnetworkaccessmanager,Qnetworkrequest,Qnetworkreply,首先,我想在创建这个类时触发一个初始化过程。(DNS查找已知主机,并通过指定数量的插槽与主机进行预握手-测试后不得硬编码)-在创建所有类、读取配置等过程中 其次,我希望根据特定主机允许(和打开)的插槽允许并行请求发送 坦率地说,下面的代码存在多个问题 谷歌似乎会重定向,当多个请求同时发送到这个主机时,似乎会导致灾难性的失败,当x number或request触发完成的信号时,该信号会调用我的重定向检查,第一个请求被重定向,而我似乎会得到第一个回复,并返回x条错误消息 出于同样的原因,我似乎无法
- 谷歌似乎会重定向,当多个请求同时发送到这个主机时,似乎会导致灾难性的失败,当x number或request触发完成的信号时,该信号会调用我的重定向检查,第一个请求被重定向,而我似乎会得到第一个回复,并返回x条错误消息
- 出于同样的原因,我似乎无法从httpFinished调用deleteLater()
- 我找不到一种方法来等待请求的槽号完成,以启动队列中的下一个请求
QT += core network
QT -= gui
TARGET = nebula
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp \
network.cpp
HEADERS += \
network.h
网络.h
#ifndef NETWORK_H
#define NETWORK_H
#include <QObject>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QHostInfo>
#include <QtNetwork/QSslConfiguration>
#include <QEventLoop>
class network : public QObject
{
Q_OBJECT
public:
explicit network(QObject *parent = 0);
~network();
void sendGet(const QUrl &url);
private:
QNetworkAccessManager networkAccessManager;
QHash<QString, QByteArray> usedSession;
QNetworkReply *reply;
QUrl url;
Q_INVOKABLE void init();
void replyFinished(QUrl &url, QNetworkReply *reply);
QList<QString> provideTcpHosts();
QList<QString> provideSslHosts();
QEventLoop eventLoop;
private slots:
void httpError();
void sslErrors(const QList<QSslError> &errors);
void httpFinished();
};
#endif // NETWORK_H
\ifndef网络
#定义网络
#包括
#包括
#包括
#包括
#包括
#包括
类网络:公共QObject
{
Q_对象
公众:
显式网络(QObject*parent=0);
~network();
void sendGet(const QUrl和url);
私人:
QNetworkAccessManager networkAccessManager;
QHash使用会话;
QNetworkReply*回复;
QUrl;
Q_可调用的void init();
void replyFinished(qrl&url,QNetworkReply*reply);
QList provideCpHosts();
QList提供了sslhosts();
QEventLoop事件循环;
专用插槽:
void httpError();
无效错误(常数列表和错误);
void httpFinished();
};
#endif//NETWORK\u H
network.cpp
#include "network.h"
/**
* @brief network::network
* initialazing pre-networking initialization once the eventpool is ready
* @param parent
*/
network::network(QObject *parent) : QObject(parent)
{
QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection);
}
network::~network()
{
networkAccessManager.deleteLater();
}
/**
* @brief network::init
* dns chache warming for all hosts and pre-connecting to all hosts via 4 sockets
*/
void network::init()
{
QList<QString> tcpHosts = provideTcpHosts();
QList<QString> sslHosts = provideSslHosts();
// tcp hosts initialazation
for( int i=0; i<tcpHosts.count(); ++i )
{
// pre-dns lookup cache warming
QHostInfo::lookupHost(tcpHosts[i], 0, 0);
qDebug() << "pre-dns lookup cache warming for: " + tcpHosts[i];
// tcp pre-handshake with known hosts over 4 sockets with known http hosts
for(int a=0; a<4; ++a)
{
networkAccessManager.connectToHost(tcpHosts[i], 80);
qDebug() << "connecting " + QString::number(a+1) + "th socket for " + tcpHosts[i];
}
}
// tcp hosts initialazation
for( int i=0; i<sslHosts.count(); ++i )
{
// pre-dns lookup cache warming
QHostInfo::lookupHost(sslHosts[i], 0, 0);
qDebug() << "pre-dns lookup cache warming for: " + sslHosts[i];
// tcp pre-handshake with known hosts over 4 sockets with known http hosts
for(int a=0; a<4; ++a)
{
networkAccessManager.connectToHostEncrypted(sslHosts[i], 443);
qDebug() << "connecting " + QString::number(a+1) + "th socket for " + sslHosts[i];
}
}
}
/**
* @brief network::replyFinished
* storing previous ssl session tickets for re-use, and error handling for finished requests
* @param url
* @param reply
*/
void network::replyFinished(QUrl &url, QNetworkReply *reply)
{
if(!usedSession.contains((QString)url.toString()))
{
usedSession.insert((QString)url.toString(), (QByteArray)reply->sslConfiguration().sessionTicket());
qDebug() << "saved ssl session ticket for" + url.toString();
}
reply->deleteLater();
}
/**
* @brief network::sendGet
* sending a simple GET request to specified url
* @param url
*/
void network::sendGet(const QUrl &url)
{
connect(&networkAccessManager, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit()));
qDebug() << "Sending a GET request to" + (QString)url.toString();
QNetworkRequest request;
request.setUrl(url);
request.setRawHeader("User-Agent", "nebula");
// reusing an ssl session ticket if exists for url
if(usedSession.contains((QString)url.toString()))
{
QSslConfiguration qssl;
qssl.setSslOption(QSsl::SslOptionDisableSessionPersistence, false);
qssl.setSessionTicket(usedSession.value((QString)url.toString()));
request.setSslConfiguration(qssl);
qDebug() << "used ssl session ticket for" + url.toString();
}
reply = networkAccessManager.get(request);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(httpError()));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>)));
connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
}
/**
* @brief network::provideTcpHosts
* @return
*/
QList<QString> network::provideTcpHosts()
{
return QList<QString>()
<< (QString)"http://www.google.com"
<< (QString)"http://www.bing.com";
}
/**
* @brief network::provideSslHosts
* @return
*/
QList<QString> network::provideSslHosts()
{
return QList<QString>()
<< (QString)"https://www.ssllabs.com/ssltest/";
}
/*SLOTS*/
/**
* @brief network::slotTcpError
* @param tcpError
*/
void network::httpError()
{
qDebug() << "QNetworkRequest::HttpStatusCodeAttribute " << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute ) << "received";
qDebug() << reply->errorString();
reply->deleteLater();
}
/**
* @brief network::slotSslErrors
* @param sslErrors
*/
void network::sslErrors(const QList<QSslError> &errors)
{
QString errorString;
foreach (const QSslError &error, errors) {
if (!errorString.isEmpty())
errorString += ", ";
errorString += error.errorString();
}
qDebug() << "ssl error recieved: " + errorString;
reply->deleteLater();
}
void network::httpFinished()
{
// possible redirect url
QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (!redirectionTarget.isNull()) {
this->sendGet(reply->url().resolved(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl()));
} else {
qDebug() << "QNetworkRequest::HttpStatusCodeAttribute " << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute ) << "received ";
qDebug() << reply->errorString();
qDebug() << reply->readAll();
}
}
#include <QCoreApplication>
#include <network.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
network networkAccessManager;
for(int i = 0; i < 50; ++i)
{
networkAccessManager.sendGet((QString)"http://www.google.com");
}
return a.exec();
}
#包括“network.h”
/**
*@brief network::network
*事件池准备就绪后初始化预联网初始化
*@param父级
*/
网络::网络(QObject*父对象):QObject(父对象)
{
QMetaObject::invokeMethod(这是“init”,Qt::QueuedConnection);
}
网络::~network()
{
networkAccessManager.deleteLater();
}
/**
*@brief network::init
*所有主机的dns检查正在升温,并通过4个套接字预连接到所有主机
*/
void网络::init()
{
QList tcpHosts=providedecphosts();
QList sslHosts=provideSslHosts();
//tcp主机初始化
对于(int i=0;ICODE风格注释):避免C样式的转换,而在RexyEnter中的转换不应该是必要的。对于并行化的请求,考虑将操作封装到一个类中,而不是试图在一个“网络”中跟踪多个命令。这就是所谓的命令模式。请参见此处,了解我在本文中发现它有用的原因:实际上,这正是QNetworkReply所做的)@Frankosterfield感谢您的回复。:)问题在于只需要一个networkManager,因为它保留缓存,如果处理正确,就不需要再次与同一台主机握手,这就是init()所做的进一步鼓励——因为握手是“新鲜的”——这将使请求次数减少一半以上,因为它消除了tcp的2次往返,ssl的4次往返,如果调用它,它可以直接发送实际的http请求。因此,将逻辑包含在一个集合中的一个类中似乎是合乎逻辑的,而将其忘记命令可以共享一个管理器,我看不出有什么问题。该类可以创建命令对象并将它们返回给调用方