我对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()
被调用,它能正常工作吗?
答案 0 :(得分:17)
我一直在浏览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()
,直到有传入连接为止。
答案 1 :(得分:0)
Qt的“幕后”功能大多发生在QCoreApplication的主要事件循环中:信号/插槽,定时器等。
一个例子是JavaScript - 您绑定一个事件,但事件循环由浏览器处理。