使用 Qt 提升 asio

Posted

技术标签:

【中文标题】使用 Qt 提升 asio【英文标题】:Boost asio with Qt 【发布时间】:2017-01-21 16:57:55 【问题描述】:

我正在尝试将boost::asio async client example 与简单的 Qt GUI 一起使用,例如:

我的应用程序中的一点 sn-p:

按钮点击SLOT:

void RestWidget::restGetCall()


    networkService ntwkSer("www.boost.org","80");
    connect(&ntwkSer, SIGNAL(responseReady(std::string)), this, SLOT(showResponse(std::string)));
    ntwkSer.get("/LICENSE_1_0.txt");


networkService类只是上面链接的boost示例代码的一个包装器。它派生自QObject类,用于信号、槽机制。

void networkService::get(const std::string & path)

   // boost::thread (boost::bind(&networkService::networkCall,this,path));//this gives me sigabrt
    networkCall(path);//this works fine, and i get output as pictured above.


void networkService::networkCall(const std::string path)

    tcp::resolver::query query(host_, port_);//also these host,port fields come out to be invalid/garbage.
    //tcp::resolver::query query("www.boost.org","80");//still doesnt resolve the SIGABRT
    resolver_.async_resolve(query,
        boost::bind(&networkService::handle_resolve, this,
        boost::asio::placeholders::error,
        boost::asio::placeholders::iterator,
        path));
    io_service.run();

问题是当我从 boost::thread 运行 io_service.run() 时。我得到了 SIGABRT。

还有他在networkService::networkCall(path)函数中的host_,port_networkService包装类字段在调试时是无效的,在构造时被保存:

networkService ntwkSer("www.boost.org","80");

boost::thread 的明显原因是让 GUI 非阻塞,因为 io_service() 有自己的事件循环。我的意图是在单独的 boost 线程中运行 boost::asio 异步调用,并使用 QT 的 Q_OBJECT 信号槽机制通知 GUI 线程。

我不明白 SIGABRT 的原因,以及为什么我开始使用 boost::thread 后 host_ 和 port_ 的字段值会变得无效。

PS:同样的设置,在类似的命令行应用程序(没有 Qt GUI 代码)中的 boost::thread 行为正确,即当 networkService 类没有被黑客入侵以使 Qt 信号/插槽通知主 GUI 线程时。在这里,我使用 boost::asio 在 boost::thread 中的响应。

编辑: 根据对我的问题的回答,我尝试了这个......我禁用了网络服务类的 Q_OBJECT 信号/插槽和 QObject 派生,以确保 MOC 不会把事情搞砸.. 但问题仍然存在,我在 Windows 上遇到访问冲突与 Linux 上的 sigabrt。网络服务对象的字段被破坏的问题也存在,最终出现访问冲突。 实际上行为没有改变。

在启动线程之前: 从内螺纹 访问冲突继续...

所以,即使没有 MOC ,问题仍然存在。

编辑 2: 我很抱歉打扰..我犯了一个巨大的错误,即在 boost::thread 中使用本地 networkService 对象,当线程实际运行时,该对象被限定了!

【问题讨论】:

我没有机会检查(为这个低价值的评论道歉),但它可能与 qt 中的预编译步骤有关吗? Boost 有很多复杂的头文件,而且 qt 在调用 gcc 之前对代码做了一些奇怪的事情,这可能是一个让事情变得混乱的机会。总的来说,我一直认为混合应用程序范式是一种糟糕的形式。 boost asio 是前摄器,qt 是信号槽。事情可能会有点混乱! 【参考方案1】:

问题在于,qt 中只允许一个线程操作 qt 中的 gui。那就是调用 QApplication::exec 的那个。这样做是为了消除 qt 用户的复杂性,并且由于 QApplication / 消息循环是单例的。话虽如此,在 qt 中,线程正在发生一些神奇的事情。所有的 QObjects 都被分配了一个线程。默认情况下,创建它们的那个。当建立信号槽连接时,将确定如何实际分派呼叫。如果对象属于同一线程,则通过直接/同步调用插槽来调度信号。如果将对象分配给可区分的线程,则消息从一个线程发送到另一个线程,以调用分配给槽所在对象的线程上的槽。这就是你在这里真正需要的。

您的代码的问题是您的两个 QObject 都是在同一个线程上创建的。因此,它们被分配了相同的线程。因此,操作 GUI 的插槽直接从您的工作线程调用,记住这是被禁止的!由于您的工作人员不是调用 QApplication::exec 的人。要在调用插槽时覆盖自动并说服正确执行线程切换,您必须在执行连接时使用 Qt::QueuedConnection。

connect(&ntwkSer, SIGNAL(responseReady(std::string)), this, SLOT(showResponse(std::string)), Qt::QueuedConnection);

【讨论】:

【参考方案2】:

很难让 asio io_service.run() 函数与 Qt 事件循环“玩得很好”。

使用调用io_service::poll()io_service::poll_one()Qt 插槽然后将该插槽连接到QTimerEvent 会更容易。

使用QNetworkAccessManager 而不是asio 更容易,请参阅Qt Client Example

【讨论】:

以上是关于使用 Qt 提升 asio的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Qt 中使用 boost::asio::deadline_timer?

使用 asio 提升线程池:线程随机不执行

提升 Asio 和 OpenSSL 1.1.0

Boost::ASIO VS Qt 网络

提升 asio async_read_some 超时

提升asio断网处理