题
我对 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()
被召唤?
解决方案
我一直在研究源代码 QtCore
和 QtNetwork
模块。
顺便说一下, QTcpServer
可以在两种模式下工作: 同步 和 异步.
在 同步 通话后模式 listen()
呼叫者可以呼叫 waitForNewConnection()
这是一种阻塞方法(线程将休眠,直到有人连接到侦听端口)。这边走 QTcpServer
可以在没有事件循环的线程中工作。
在 异步 模式 QTcpServer
将发出 newConnection()
当接受新连接时发出信号。但为了能够做到这一点,必须运行一个事件循环。底层的 QCoreApplication
是 QEventLoop
和 QAbstractEventDispatcher
(抽象类,具体类型取决于操作系统,例如 QEventDispatcherUNIX
)。该事件调度程序可以监视套接字上的条件(由文件描述符表示)。它有一个方法 registerSocketNotifier(QSocketNotifier*)
. 。该方法由构造函数调用 QSocketNotifier
类,其中 QTcpServer
每次都会创建一个实例 listen()
叫做。唯一的系统调用被调用时 QTcpServer::listen()
当然,被调用的是 listen()
它立即返回,所有真正的魔法都发生在事件循环开始运行时。事件循环(使用调度程序)将监视已注册的套接字是否存在特定条件。它称为 select()
一种系统调用,它接收一个或多个要在某些条件下(如果有数据要读取、是否可以写入数据或是否发生错误)(由内核)监视的文件描述符。该调用可以阻塞线程,直到满足套接字上的条件,或者它可以在经过一段时间并且不满足套接字上的条件后返回。我不确定 Qt 是否正在调用 select()
提供或不提供等待时间(无限期阻塞),我认为它是以某种复杂的方式确定的并且是可变的。因此,当最终满足套接字上的条件时,事件调度程序将通知 QSocketNotifier
对于该套接字,这将通知 QTcpServer
谁正在监听套接字,它将接受连接,并发出 newConnection()
信号。
所以 QTcpServer
本身并不调用事件循环/套接字监控系统,但它通过以下方式依赖于它: QSocketNotifier
它用于异步接收连接。
当同步方法 waitForNewConnection()
被称为它只是绕过所有 QSocketNotifier
东西和电话 accept()
它会阻塞线程,直到有传入连接为止。
其他提示
Qt 的大部分“幕后”功能发生在 QCoreApplication 的主事件循环内:信号/槽、定时器等
一个例子是 JavaScript - 您绑定一个事件,但事件循环由浏览器处理。