平时,我们检测网络是否连接,一般都是用ping命令。实际开发中,经常也需要检测网络状态。最近之项目过程中就需要实现这一的功能,在网络断开时给出相应的用户提示。QHostInfo给我们提供了一个方便的接口,实现这一的功能。话不多说,先上一段代码demo
NetWorkTest *NetWorkTest::GetInstance()
{
static NetWorkTest instance;
return &instance;
}
void NetWorkTest::checkNetWork()
{
QHostInfo::lookupHost("www.baidu.com",this,SLOT(lookedUpSlot(QHostInfo)));
qDebug()<< "lookupHost";
}
NetWorkTest::NetWorkTest(QObject *parent) : QObject(parent)
{
}
void NetWorkTest::lookedUpSlot(QHostInfo hostInfo)
{
qDebug()<<__FUNCTION__;
bool status = false;
if(hostInfo.error() == QHostInfo::NoError)
{
status = true;
}
emit netWorkStateChanged(status);
}
//main.cpp
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
QQmlContext *context=engine.rootContext();
context->setContextProperty("netWorkTest", NetWorkTest::GetInstance());
engine.load(url);
return app.exec();
}
//main.qml
Window {
visible: true
width: 640
height: 480
// title: qsTr("Hello World")
property bool connected: true//qsTr("网络已连接")
RowLayout {
id: layout
// anchors.fill: parent
width: 200
anchors.centerIn: parent
Button{
text: qsTr("检测网络")
onClicked: {
netWorkTest.checkNetWork();
}
}
Label{
text: qsTr("网络状态:")
}
Label{
id: status
text: {
if(connected)
{
return qsTr("网络已连接")
}
else{
return qsTr("网络已断开")
}
}
}
}
Connections{
target: netWorkTest
onNetWorkStateChanged:{
console.log('isConnected: ',isConnected)
connected = isConnected;
}
}
}
这样,基本实现我们需要的结果。但是,实际测试过程中发现,断开网络之后,再检测,还是返回true。着就让人纳闷。更郁闷的是,从QHostInfo的说明文档找不到任何相关的说明。个人猜测就是有缓存。要找到真正的原因,只能看QHostInfo的实现代码了。
int QHostInfo::lookupHost(const QString &name, QObject *receiver,
const char *member)
{
//此处省略不重要的代码
//以下才是我们要关注的核心代码
QHostInfoLookupManager *manager = theHostInfoLookupManager();
if (manager) {
// the application is still alive
if (manager->cache.isEnabled()) {
// check cache first
bool valid = false;
QHostInfo info = manager->cache.get(name, &valid);
if (valid) {
if (!receiver)
return -1;
info.setLookupId(id);
QHostInfoResult result;
QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
result.emitResultsReady(info);
return id;
}
}
// cache is not enabled or it was not in the cache, do normal lookup
QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id);
if (receiver)
QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
manager->scheduleLookup(runnable);
}
return id;
}
如此,我们清楚QHostInfo确实三用了缓存。既然三缓存,我们有没有办法清除这些缓存呢。继续深入探讨源码:
// cache for 60 seconds
// cache 128 items
QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(128)
{
#ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT
enabled = false;
#endif
}
bool QHostInfoCache::isEnabled()
{
return enabled;
}
// this function is currently only used for the auto tests
// and not usable by public API
void QHostInfoCache::setEnabled(bool e)
{
enabled = e;
}
看看这实现,显然是没办法了。没有对外的API可以清除缓存或者修改缓存过期时间。既然如此,想要解决这个问题,就只能自己实现ping操作了。
void pingHost(NetWorkTest* instance)
{
QProcess exc;
QTextCodec *codec = QTextCodec::codecForName("GBK");
QString cmdstr="ping www.baidu.com";
exc.start(cmdstr);
exc.waitForFinished(-1);
QString outstr=codec->toUnicode(exc.readAll());
qDebug()<netWorkStateChanged(status);
}
void NetWorkTest::checkNetWork()
{
QFuture future = QtConcurrent::run(pingHost,this);
}
这里用到了QtConcurrent实现多线程,防止卡住界面。
https://github.com/huanghaining/NetWork