【问题标题】:How is QTcpServer really listening for connectionsQTcpServer 如何真正监听连接
【发布时间】:2012-07-24 19:16:42
【问题描述】:

我对 QTcpServer 在线程和阻塞方面的幕后工作方式很感兴趣。 QTcpServer 有一个 listen() 立即返回的方法。如果侦听成功开始,服务器将发出信号newConnection()。当listen() 方法返回时,我对服务器如何监听(是否在主线程上)感兴趣。带有 QTcpServer 的控制台应用程序的通常示例是这样的:

//main.cpp
int main(int argc, char* argv[])
{
    QCoreApplication app;
    MyServer server;
    app.exec();
}

//MyServer.cpp
MyServer::MyServer(QObject *parent) : QObject(parent)
{
    this->server = new QTcpServer(this);
    connect(server, SIGNAL(newConnection()), this, SLOT(on_newConnection()));
    if (!server->listen(QHostAddress::Any, 1234))
        //do something in case of error
}
void MyServer::on_newConnection()
{
    QTcpSocket* socket = server->nextPendingConnection();
    //do some communication...
}

QTcpServer 是否依赖于现有的 QCoreApplication(或者可能是 QRunLoop)并正在运行以接收网络事件。如果不调用QCoreApplication::exec(),它可以正常工作吗?

【问题讨论】:

    标签: qt qtcpserver


    【解决方案1】:

    我一直在钻研QtCoreQtNetwork 模块的源代码。

    显然,QTcpServer 可以在两种模式下工作:同步异步

    同步模式下,调用listen()后,调用者可以调用waitForNewConnection(),这是一个阻塞方法(线程会休眠直到有人连接到监听端口)。这样QTcpServer 可以在没有事件循环的线程中工作。

    异步模式下,QTcpServer 将在接受新连接时发出newConnection() 信号。但是为了能够做到这一点,必须有一个事件循环在运行。 QCoreApplication 的基础是 QEventLoopQAbstractEventDispatcher(抽象类,具体类型取决于操作系统,例如 QEventDispatcherUNIX)。此事件调度程序可以监视套接字上的条件(由文件描述符表示)。它有一个方法registerSocketNotifier(QSocketNotifier*)。此方法由QSocketNotifier 类的构造函数调用,QTcpServer 每次调用listen() 时都会创建一个实例。调用QTcpServer::listen() 时唯一调用的系统调用当然是listen(),它会立即返回,所有真正的魔法都在事件循环开始运行时发生。事件循环(使用调度程序)将监视已注册的套接字上是否存在特定条件。它调用 select() 系统调用,该系统调用接收一个或多个文件描述符(由内核)在某些条件下(如果有要读取的数据,是否可以写入数据,或者如果发生了错误)。该调用可以阻塞线程,直到满足套接字上的条件,或者它可以在经过一段时间后返回,并且不满足套接字上的条件。我不确定 Qt 是否在提供或不提供等待时间的情况下调用 select()(无限期阻塞),我认为它是以某种复杂的方式确定的并且是可变的。因此,当最终满足套接字的条件时,事件调度程序将通知该套接字的QSocketNotifier,然后通知正在侦听套接字的QTcpServer,后者将接受连接并发出newConnection() 信号。


    因此QTcpServer 本身并不调用事件循环/套接字监控系统,而是通过QSocketNotifier 依赖它,用于异步接收连接。

    当同步方法waitForNewConnection() 被调用时,它只是绕过所有QSocketNotifier 的东西并调用accept(),这会阻塞线程直到有传入连接。

    【讨论】:

    • 非常好。现在我明白了为什么在 Qt(我使用 PyQt)中执行应用程序事件循环之前调用 listen 会导致没有得到newConnection 信号。感谢您的挖掘。
    【解决方案2】:

    Qt 的大部分“幕后”功能都发生在 QCoreApplication 的主事件循环中:信号/槽、定时器等。
    一个例子是 JavaScript - 你绑定一个事件,但事件循环由浏览器处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-08
      • 1970-01-01
      • 1970-01-01
      • 2018-09-09
      • 1970-01-01
      • 2012-08-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多