8

我对 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()它可以在不被调用的情况下正常工作吗?

4

2 回答 2

17

我一直在研究QtCoreandQtNetwork模块的源代码。

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

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

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


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

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

于 2012-07-27T16:31:33.930 回答
0

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

于 2012-07-26T12:27:12.353 回答