帮助文档
专业提供香港服务器、香港云服务器、香港高防服务器租用、香港云主机、台湾服务器、美国服务器、美国云服务器vps租用、韩国高防服务器租用、新加坡服务器、日本服务器租用 一站式全球网络解决方案提供商!专业运营维护IDC数据中心,提供高质量的服务器托管,服务器机房租用,服务器机柜租用,IDC机房机柜租用等服务,稳定、安全、高性能的云端计算服务,实时满足您的多样性业务需求。 香港大带宽稳定可靠,高级工程师提供基于服务器硬件、操作系统、网络、应用环境、安全的免费技术支持。
服务器资讯 / 香港服务器租用 / 香港VPS租用 / 香港云服务器 / 美国服务器租用 / 台湾服务器租用 / 日本服务器租用 / 官方公告 / 帮助文档
从源码角度透视QTcpServer:解构QTcpServer的底层原理与技术细节
发布时间:2024-03-03 05:53:45   分类:帮助文档
从源码角度透视QTcpServer:解构QTcpServer的底层原理与技术细节 深入了解QTcpServer的底层原理和技术细节 一、背景二、QTcpServer的基本原理2.1、TCP协议简介2.2、QTcpServer的概念 三、QTcpServer源码解析3.1、QTcpServer的构造函数3.2、调用listen函数启动tcpserver3.3、QSocketNotifier的实现 总结 一、背景 QTcpServer是Qt网络模块中的一个网络通信类,用于创建TCP服务器,允许应用程序监听并处理传入的TCP连接请求。QTcpServer的作用: QTcpServer提供了一个简单而强大的方式来实现服务器端的网络通信,轻松地创建TCP服务器应用程序。 QTcpServer能够处理多个客户端同时连接,通过多线程或事件循环等机制实现并发处理,提高服务器端的性能和效率。 QTcpServer封装了TCP协议的复杂细节,提供了更高级别的接口,简化了网络编程的复杂性。 通过QTcpServer可以构建稳定可靠的网络服务,如实时通讯、远程监控、数据传输等涉及网络通信的应用场景。 示例:使用QTcpServer实现一个基本的TCP服务器。 // main.cpp #include #include "mytcpserver.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MyTcpServer server; server.startServer(); return a.exec(); } // mytcpserver.h #ifndef MYTCPSERVER_H #define MYTCPSERVER_H #include #include #include class MyTcpServer : public QTcpServer { Q_OBJECT public: MyTcpServer(QObject *parent = nullptr); void startServer(); protected: void incomingConnection(qintptr socketDescriptor) override; private slots: void onNewConnection(); void onReadyRead(); void onDisconnected(); private: QTcpSocket *clientSocket; }; #endif // MYTCPSERVER_H // mytcpserver.cpp #include "mytcpserver.h" MyTcpServer::MyTcpServer(QObject *parent) : QTcpServer(parent), clientSocket(nullptr) { connect(this, &MyTcpServer::newConnection, this, &MyTcpServer::onNewConnection); } void MyTcpServer::startServer() { if (!this->listen(QHostAddress::Any, 1234)) { qDebug() << "Server could not start!"; } else { qDebug() << "Server started!"; } } void MyTcpServer::incomingConnection(qintptr socketDescriptor) { clientSocket = new QTcpSocket(this); if (!clientSocket->setSocketDescriptor(socketDescriptor)) { qDebug() << "Error in setting socket descriptor"; return; } connect(clientSocket, &QTcpSocket::readyRead, this, &MyTcpServer::onReadyRead); connect(clientSocket, &QTcpSocket::disconnected, this, &MyTcpServer::onDisconnected); qDebug() << "Client connected"; } void MyTcpServer::onNewConnection() { qDebug() << "New connection available"; } void MyTcpServer::onReadyRead() { QByteArray data = clientSocket->readAll(); qDebug() << "Data received: " << data; } void MyTcpServer::onDisconnected() { qDebug() << "Client disconnected"; } 使用Qt的构建工具qmake和make(或者使用Qt Creator集成开发环境)编译。在项目文件夹中创建一个.pro文件(例如:mytcpserver.pro),内容如下: QT += core network CONFIG += c++11 TARGET = mytcpserver CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp \ mytcpserver.cpp HEADERS += mytcpserver.h 执行编译命令: qmake mytcpserver.pro make 可以看到,使用QTcpServer很容易就实现了一个TCP服务器,而且它使用异步事件的方式处理TCP客户端的连接,那么它是如何实现的异步机制呢?想了解QT的socket是基于什么模型来实现的,博主同样非常的感兴趣,所以看了QT关于TcpServer实现的相关源码,现在将所了解的内容记录下来。 二、QTcpServer的基本原理 2.1、TCP协议简介 TCP(Transmission Control Protocol,传输控制协议)是因特网协议套件中的一部分,它位于传输层,提供可靠的、面向连接的数据传输服务。 TCP协议的特点: 面向连接:在进行数据传输之前,TCP在通信双方之间建立连接,之后才会开始数据的传输。连接建立包括三步握手,以确保通信的正常进行。 可靠性:TCP协议通过序号、确认和重传等机制来确保数据的可靠传输。如果一个数据包未能正确传输,TCP会进行重传以保证数据的完整性。 流控制:TCP协议通过滑动窗口机制来进行流控制,确保发送方和接收方之间的数据传输速率相匹配,避免数据包的过载和丢失。 拥塞控制:TCP通过拥塞窗口和拥塞避免等机制来控制网络拥塞,避免过多的数据流量导致的网络拥堵,从而保证网络的稳定性和可靠性。 面向字节流:TCP是面向字节流的协议,它不会将数据分割成固定大小的数据包,而是按照应用程序传送的字节流来进行数据传输。 TCP协议提供了一种高可靠性的数据传输方式,适用于要求数据传输可靠性和顺序性的应用场景,如文件传输、电子邮件发送等。但是,与UDP相比,TCP在数据传输过程中有较高的开销。 2.2、QTcpServer的概念 QTcpServer是Qt框架中用于实现TCP服务器的类。它提供了一种简单而高效的方式,用于监听传入的TCP连接请求,从而可以与客户端建立连接并实现数据交换。 QTcpServer的主要作用: QTcpServer可以通过调用listen()方法,在指定的IP地址和端口上开始监听传入的连接请求。监听到连接请求后通过nextPendingConnection()方法接受这些请求,获得一个QTcpSocket对象,用于与客户端进行数据交换。通过重写incomingConnection()方法,在建立新连接时执行自定义的处理操作。QTcpServer可以管理多个TCP连接,并在需要的时候进行数据交换或断开连接。通过信号和槽机制,QTcpServer可以处理连接建立、断开、数据到达等事件,实现灵活的连接管理和数据处理。 QTcpSocket同样是Qt框架中用于实现TCP网络通信的重要类。QTcpServer与QTcpSocket的关系: QTcpServer是用于实现TCP服务器的类,它负责监听传入的TCP连接请求,并与客户端建立连接;QTcpSocket则是用于实现TCP客户端的类,它负责与服务器建立连接并进行数据交换。 当QTcpServer监听到传入的连接请求时,它会返回一个QTcpSocket对象,该对象用于与客户端进行数据交换。这个QTcpSocket对象是表示与客户端建立的连接的句柄,通过它可以实现数据的发送和接收。 QTcpServer可以管理多个QTcpSocket连接,接受多个客户端的连接请求,每个连接都有一个对应的QTcpSocket对象。 QTcpSocket对象在与服务器建立连接后,可以向服务器发送数据,也可以接收来自服务器的数据。服务器端的QTcpServer则可以接受来自客户端的数据,并向客户端发送数据。 三、QTcpServer源码解析 Qt源码下载: git clone https://code.qt.io/qt/qt5.git # cloning the repo cd qt5 git checkout 5.14.2 # checking out the specific release or branch perl init-repository 3.1、QTcpServer的构造函数 先从QTcpServer的构造函数来看,下面是QTcpServer的构造函数原型: QTcpServer::QTcpServer(QObject *parent) : QObject(*new QTcpServerPrivate, parent) { Q_D(QTcpServer); #if defined(QTCPSERVER_DEBUG) qDebug("QTcpServer::QTcpServer(%p)", parent); #endif d->socketType = QAbstractSocket::TcpSocket; } 首先创建了一个QTcpServerPrivate的参数类。在QT源码中,每个类都有一个参数类,类名就是原类名加上Private。这个类主要放着QTcpServer类会用到的一些成员对象,而QTcpServer类里面只会定义方法,不会有成员对象。QTcpServerPrivate类的定义: // qtcpserver_p.h class Q_NETWORK_EXPORT QTcpServerPrivate : public QObjectPrivate, public QAbstractSocketEngineReceiver { Q_DECLARE_PUBLIC(QTcpServer) public: QTcpServerPrivate(); ~QTcpServerPrivate(); QList pendingConnections; quint16 port; QHostAddress address; QAbstractSocket::SocketType socketType; QAbstractSocket::SocketState state; QAbstractSocketEngine *socketEngine; QAbstractSocket::SocketError serverSocketError; QString serverSocketErrorString; int maxConnections; #ifndef QT_NO_NETWORKPROXY QNetworkProxy proxy; QNetworkProxy resolveProxy(const QHostAddress &address, quint16 port); #endif virtual void configureCreatedSocket(); // from QAbstractSocketEngineReceiver void readNotification() override; void closeNotification() override { readNotification(); } void writeNotification() override {} void exceptionNotification() override {} void connectionNotification() override {} #ifndef QT_NO_NETWORKPROXY void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *) override {} #endif }; 然后QTcpServer构造函数内部实现就很简单了。Q_D(QTcpServer)宏实际上就是取到QTcpServerPrivate对象的指针赋给变量d: #define Q_D(Class) ClassPrivate * const d = d_func() #define Q_Q(Class) Class * const q = q_func() d->socketType = QAbstractSocket::TcpSocket把套接字类型设置为Tcp。 至此,QTcpServer构造函数的工作结束。 3.2、调用listen函数启动tcpserver 一旦调用listen函数,tcpserver就开始运行了。接下来,连接、接收数据和发送数据的完成都可以通过信号来接收。那么,QT具体是如何实现等待连接和等待接收数据的呢?而且对于不同的平台又是如何实现的呢?我们来分析一下listen函数究竟都做了些什么工作。 (1)首先判断是否已是监听状态,是的话就直接返回。 Q_D(QTcpServer); if (d->state == QAbstractSocket::ListeningState) { qWarning("QTcpServer::listen() called when already listening"); return false; } (2)接着设置协议类型,IP地址、端口号。 QAbstractSocket::NetworkLayerProtocol proto = address.protocol(); QHostAddress addr = address; #ifdef QT_NO_NETWORKPROXY static const QNetworkProxy &proxy = *(QNetworkProxy *)0; #else QNetworkProxy proxy = d->resolveProxy(addr, port); #endif (3)然后创建了一个socketEngine对象,它的类型是QAbstractSocketEngine。QAbstractSocketEngine定义了很多与原始套接字机制相似的函数,比如bind、listen、accept等方法,还实现了waitForRead、writeDatagram、read等函数。所以当我们调用QSocket的读写方法时,实际上是由QAbstractSocketEngine类来实现的。不过,QAbstractSocketEngine本身是一个抽象类,不能直接实例化。在listen函数中,我们调用了QAbstractSocketEngine类的静态函数createSocketEngine来创建对象。 delete d->socketEngine; d->socketEngine = QAbstractSocketEngine::createSocketEngine(d->socketType, proxy, this); if (!d->socketEngine) { d->serverSocketError = QAbstractSocket::UnsupportedSocketOperationError; d->serverSocketErrorString = tr("Operation on socket is not supported"); return false; } 重点看一下createSocketEngine具体是怎么实现的: QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(qintptr socketDescripter, QObject *parent) { QMutexLocker locker(&socketHandlers()->mutex); for (int i = 0; i < socketHandlers()->size(); i++) { if (QAbstractSocketEngine *ret = socketHandlers()->at(i)->createSocketEngine(socketDescripter, parent)) return ret; } return new QNativeSocketEngine(parent); } 在类似递归的所有条件判断之后,最终返回一个QNativeSocketEngine对象。QNativeSocketEngine继承了QAbstractSocketEngine类,并实现了QAbstractSocketEngine的所有功能。在这个类的具体代码中可以看到一些做平台判断的代码,以及与平台相关的套接字函数。QNativeSocketEngine的实现并不只是一个文件,它包括qnativesocketengine_unix.cpp、qnativesocketengine_win.cpp和qnativesocketengine_winrt.cpp。因此,当在Windows平台编译程序时,编译器会包含qnativesocketengine_win.cpp文件,在Linux下编译时会包含qnativesocketengine_unix.cpp文件。QT通过一个抽象类和不同平台的子类来实现跨平台的套接字机制。 (4)继续回到TcpServer的listen函数,创建了一个socketEngine对象后开始调用bind、listen等函数来完成最终的socket设置。 #ifndef QT_NO_BEARERMANAGEMENT //copy network session down to the socket engine (if it has been set) d->socketEngine->setProperty("_q_networksession", property("_q_networksession")); #endif if (!d->socketEngine->initialize(d->socketType, proto)) { d->serverSocketError = d->socketEngine->error(); d->serverSocketErrorString = d->socketEngine->errorString(); return false; } proto = d->socketEngine->protocol(); if (addr.protocol() == QAbstractSocket::AnyIPProtocol && proto == QAbstractSocket::IPv4Protocol) addr = QHostAddress::AnyIPv4; d->configureCreatedSocket(); if (!d->socketEngine->bind(addr, port)) { d->serverSocketError = d->socketEngine->error(); d->serverSocketErrorString = d->socketEngine->errorString(); return false; } if (!d->socketEngine->listen()) { d->serverSocketError = d->socketEngine->error(); d->serverSocketErrorString = d->socketEngine->errorString(); return false; } (5)接着开始设置信号接收,setReceiver传入TcpServerPrivate对象,从函数名可以看出是设置一个接收信息的对象,所以当套接字有新信息时,就会回调TcpServerPrivate对象的相关函数来实现消息通知。设置完消息接收对象以后,调用setReadNotificationEnabled(true)来启动消息监听。 d->socketEngine->setReceiver(d); d->socketEngine->setReadNotificationEnabled(true); setReadNotificationEnabled函数的实现如下: void QNativeSocketEngine::setReadNotificationEnabled(bool enable) { Q_D(QNativeSocketEngine); if (d->readNotifier) { d->readNotifier->setEnabled(enable); } else if (enable && d->threadData->hasEventDispatcher()) { d->readNotifier = new QReadNotifier(d->socketDescriptor, this); d->readNotifier->setEnabled(true); } } 这个函数是创建了一个QReadNotifier对象,QReadNotifier的定义如下: class QReadNotifier : public QSocketNotifier { public: QReadNotifier(qintptr fd, QNativeSocketEngine *parent) : QSocketNotifier(fd, QSocketNotifier::Read, parent) { engine = parent; } protected: bool event(QEvent *) override; QNativeSocketEngine *engine; }; bool QReadNotifier::event(QEvent *e) { if (e->type() == QEvent::SockAct) { engine->readNotification(); return true; } else if (e->type() == QEvent::SockClose) { engine->closeNotification(); return true; } return QSocketNotifier::event(e); } QReadNotifier其实就是继承了QSocketNotifier。QSocketNotifier是一个消息处理类,主要用来监听文件描述符的活动。也就是说,当文件描述符状态发生变化时,就会触发相应的信息。它可以监听三种状态:Read(读)、Write(写)、Exception(异常)。而我们这里用到的QReadNotifier主要是监听Read事件,也就是说当套接字句柄有可读消息时(连接信息也是可读信息的一种),就会调用event函数。在event函数中,我们回调了engine->readNotification()函数。readNotification函数的实现如下: void QTcpServerPrivate::readNotification() { Q_Q(QTcpServer); for (;;) { if (pendingConnections.count() >= maxConnections) { #if defined (QTCPSERVER_DEBUG) qDebug("QTcpServerPrivate::_q_processIncomingConnection() too many connections"); #endif if (socketEngine->isReadNotificationEnabled()) socketEngine->setReadNotificationEnabled(false); return; } int descriptor = socketEngine->accept(); if (descriptor == -1) { if (socketEngine->error() != QAbstractSocket::TemporaryError) { q->pauseAccepting(); serverSocketError = socketEngine->error(); serverSocketErrorString = socketEngine->errorString(); emit q->acceptError(serverSocketError); } break; } #if defined (QTCPSERVER_DEBUG) qDebug("QTcpServerPrivate::_q_processIncomingConnection() accepted socket %i", descriptor); #endif q->incomingConnection(descriptor); QPointer that = q; emit q->newConnection(); if (!that || !q->isListening()) return; } } 在这个函数里面调用了socketEngine->accept()来获取套接字句柄,然后将其传递给q->incomingConnection(descriptor)来创建QTcpSocket对象。最后发送了emit q->newConnection()信号。如果使用过QTcpServer,那么对这个信号应该很熟悉。 因此,QT通过内部消息机制实现了套接字的异步通信。并且,对外提供的函数既支持同步机制,也支持异步机制。调用者可以选择通过信号槽机制来实现异步,也可以调用类似waitforread、waitforconnect等函数来实现同步等待。实际上,waitforread等同步函数是通过函数内部的循环来检查消息标志。当标志为可读或者函数超时时则返回。 3.3、QSocketNotifier的实现 在前面提到了使用QSocketNotifier,可以在套接字有可读或可写信号时调用event函数来实现异步通知。但是,QSocketNotifier又是如何知道套接字什么时候发生变化的呢?QSocketNotifier的实现和QT的消息处理机制是密切相关的。要完全讲清楚这一点,就必须涉及到QT的消息机制。 这里只把比较关键的代码抽取出来分析一下。首先,不同平台的消息处理机制都是不一样的,所以QSocketNotifier在不同平台下的实现也是不一样的。我们主要看一下Linux平台下是如何实现的。 (1)QSocketNotifier类的声明和定义,用于通知状况以支持异步 IO(输入/输出)操作。 class QSocketNotifierPrivate; class Q_CORE_EXPORT QSocketNotifier : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QSocketNotifier) public: enum Type { Read, Write, Exception }; QSocketNotifier(qintptr socket, Type, QObject *parent = nullptr); ~QSocketNotifier(); qintptr socket() const; Type type() const; bool isEnabled() const; public Q_SLOTS: void setEnabled(bool); Q_SIGNALS: void activated(int socket, QPrivateSignal); protected: bool event(QEvent *) override; private: Q_DISABLE_COPY(QSocketNotifier) }; QSocketNotifier类同时声明了一个嵌套类QSocketNotifierPrivate,这个类用来实现QSocketNotifier的私有方法和属性。QSocketNotifier类继承自QObject类,并且使用了Q_OBJECT宏来支持信号和槽机制以及元对象特性。 QSocketNotifier类还提供了一些函数,用于获取套接字句柄、设置套接字的类型(可读、可写、异常)、获取状态等。可以通过setEnable(bool)槽函数来设置通知器的状态(开启或关闭)。通过activated(int socket, QPrivateSignal)信号来通知有关套接字状态的改变。采用了Q_DISABLE_COPY宏来禁止类的复制构造函数和赋值运算符的使用。 (2)SocketNotifier构造函数: QSocketNotifier::QSocketNotifier(qintptr socket, Type type, QObject *parent) : QObject(*new QSocketNotifierPrivate, parent) { Q_D(QSocketNotifier); d->sockfd = socket; d->sntype = type; d->snenabled = true; if (socket < 0) qWarning("QSocketNotifier: Invalid socket specified"); else if (!d->threadData->hasEventDispatcher()) qWarning("QSocketNotifier: Can only be used with threads started with QThread"); else d->threadData->eventDispatcher.loadRelaxed()->registerSocketNotifier(this); } QSocketNotifier的构造函数需要传入一个套接字句柄以及要监听的类型,比如读、写或错误。然后在构造函数里调用了QSocketNotifierPrivate的registerSocketNotifier函数,将自己注册进去。这样当有消息触发时,就能调用这个对象的event函数了。 (3)registerSocketNotifier函数: /* QEventDispatcher implementations for UNIX */ void QEventDispatcherUNIX::registerSocketNotifier(QSocketNotifier *notifier) { Q_ASSERT(notifier); int sockfd = notifier->socket(); QSocketNotifier::Type type = notifier->type(); #ifndef QT_NO_DEBUG if (notifier->thread() != thread() || thread() != QThread::currentThread()) { qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); return; } #endif Q_D(QEventDispatcherUNIX); QSocketNotifierSetUNIX &sn_set = d->socketNotifiers[sockfd]; if (sn_set.notifiers[type] && sn_set.notifiers[type] != notifier) qWarning("%s: Multiple socket notifiers for same socket %d and type %s", Q_FUNC_INFO, sockfd, socketType(type)); sn_set.notifiers[type] = notifier; } 在这个函数里面主要是将对象和套接字句柄sockfd作为映射放入socketNotifiers里面。 QHash socketNotifiers; (4)processEvents函数处理所有消息,Linux平台的实现如下: bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherUNIX); d->interrupt.storeRelaxed(0); // we are awake, broadcast it emit awake(); QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); const bool include_timers = (flags & QEventLoop::X11ExcludeTimers) == 0; const bool include_notifiers = (flags & QEventLoop::ExcludeSocketNotifiers) == 0; const bool wait_for_events = flags & QEventLoop::WaitForMoreEvents; const bool canWait = (d->threadData->canWaitLocked() && !d->interrupt.loadRelaxed() && wait_for_events); if (canWait) emit aboutToBlock(); if (d->interrupt.loadRelaxed()) return false; timespec *tm = nullptr; timespec wait_tm = { 0, 0 }; if (!canWait || (include_timers && d->timerList.timerWait(wait_tm))) tm = &wait_tm; d->pollfds.clear(); d->pollfds.reserve(1 + (include_notifiers ? d->socketNotifiers.size() : 0)); if (include_notifiers) for (auto it = d->socketNotifiers.cbegin(); it != d->socketNotifiers.cend(); ++it) d->pollfds.append(qt_make_pollfd(it.key(), it.value().events())); // This must be last, as it's popped off the end below d->pollfds.append(d->threadPipe.prepare()); int nevents = 0; switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), tm)) { case -1: perror("qt_safe_poll"); break; case 0: break; default: nevents += d->threadPipe.check(d->pollfds.takeLast()); if (include_notifiers) nevents += d->activateSocketNotifiers(); break; } if (include_timers) nevents += d->activateTimers(); // return true if we handled events, false otherwise return (nevents > 0); } 可以看到一个处理套接字相关的函数qt_safe_poll。看看它的内部实现: int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts) { if (!timeout_ts) { // no timeout -> block forever int ret; EINTR_LOOP(ret, qt_ppoll(fds, nfds, nullptr)); return ret; } timespec start = qt_gettime(); timespec timeout = *timeout_ts; // loop and recalculate the timeout as needed forever { const int ret = qt_ppoll(fds, nfds, &timeout); if (ret != -1 || errno != EINTR) return ret; // recalculate the timeout if (!time_update(&timeout, start, *timeout_ts)) { // timeout during update // or clock reset, fake timeout error return 0; } } } qt_safe_poll调用了qt_ppoll,qt_ppoll的定义如下: static inline int qt_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts) { #if QT_CONFIG(poll_ppoll) || QT_CONFIG(poll_pollts) return ::ppoll(fds, nfds, timeout_ts, nullptr); #elif QT_CONFIG(poll_poll) return ::poll(fds, nfds, timespecToMillisecs(timeout_ts)); #elif QT_CONFIG(poll_select) return qt_poll(fds, nfds, timeout_ts); #else // configure.json reports an error when everything is not available #endif } 这里通过QT_CONFIG的标志来判断采用哪种实现。qt_poll是QT自己的函数,在早期的版本中可能都是用select模式。但是从QT5.7开始,采用了poll模式。博主使用的是QT5.14.2版本,也是采用的poll模式。选择poll模式的原因是因为select模式监听的套接字长度是用的定长数组,无法在运行时扩展。一旦套接字数量超过FD_SETSIZE就会返回错误,在Linux默认的设置中,FD_SETSIZE是1024。 qt_poll的实现如下,内部使用的是select: int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts) { if (!fds && nfds) { errno = EFAULT; return -1; } fd_set read_fds, write_fds, except_fds; struct timeval tv, *ptv = 0; if (timeout_ts) { tv = timespecToTimeval(*timeout_ts); ptv = &tv; } int n_bad_fds = 0; for (nfds_t i = 0; i < nfds; i++) { fds[i].revents = 0; if (fds[i].fd < 0) continue; if (fds[i].events & QT_POLL_EVENTS_MASK) continue; if (qt_poll_is_bad_fd(fds[i].fd)) { // Mark bad file descriptors that have no event flags set // here, as we won't be passing them to select below and therefore // need to do the check ourselves fds[i].revents = POLLNVAL; n_bad_fds++; } } forever { const int max_fd = qt_poll_prepare(fds, nfds, &read_fds, &write_fds, &except_fds); if (max_fd < 0) return max_fd; if (n_bad_fds > 0) { tv.tv_sec = 0; tv.tv_usec = 0; ptv = &tv; } const int ret = ::select(max_fd, &read_fds, &write_fds, &except_fds, ptv); if (ret == 0) return n_bad_fds; if (ret > 0) return qt_poll_sweep(fds, nfds, &read_fds, &write_fds, &except_fds); if (errno != EBADF) return -1; // We have at least one bad file descriptor that we waited on, find out which and try again n_bad_fds += qt_poll_mark_bad_fds(fds, nfds); } } 其实Linux还有更高效的IO多路复用器,叫做epoll。 (5)activateSocketNotifiers函数处理事件: int QEventDispatcherUNIXPrivate::activateSocketNotifiers() { markPendingSocketNotifiers(); if (pendingNotifiers.isEmpty()) return 0; int n_activated = 0; QEvent event(QEvent::SockAct); while (!pendingNotifiers.isEmpty()) { QSocketNotifier *notifier = pendingNotifiers.takeFirst(); QCoreApplication::sendEvent(notifier, &event); ++n_activated; } return n_activated; } 在processEvents函数中调用了qt_safe_poll来检查是否有套接字事件。如果有事件需要处理,就会调用activateSocketNotifiers函数,而在这个函数中会通过QCoreApplication::sendEvent(notifier, &event)将消息反馈给QSocketNotifier。 通过这个过程,可以了解到Qt在Linux下使用select或者poll来实现输入输出复用的具体流程。但是具体采用哪种方式取决于使用的Qt版本。 总结 针对Qt 5.14.2的QTcpServer源码分析,QTcpServer的异步事件默认采用的poll模式(通过QT_CONFIG的标志来判断采用哪种实现,在早期的版本中可能都是用select模式。但是从QT5.7开始,采用了poll模式),poll模式解决了select模式监听的套接字长度是定长数组的问题,但是对事件的响应还是通过轮询的方式。 QTcpServer的调用函数栈: #mermaid-svg-43JTaOPjtdykoNdJ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-43JTaOPjtdykoNdJ .error-icon{fill:#552222;}#mermaid-svg-43JTaOPjtdykoNdJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-43JTaOPjtdykoNdJ .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-43JTaOPjtdykoNdJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-43JTaOPjtdykoNdJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-43JTaOPjtdykoNdJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-43JTaOPjtdykoNdJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-43JTaOPjtdykoNdJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-43JTaOPjtdykoNdJ .marker.cross{stroke:#333333;}#mermaid-svg-43JTaOPjtdykoNdJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-43JTaOPjtdykoNdJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-43JTaOPjtdykoNdJ .cluster-label text{fill:#333;}#mermaid-svg-43JTaOPjtdykoNdJ .cluster-label span{color:#333;}#mermaid-svg-43JTaOPjtdykoNdJ .label text,#mermaid-svg-43JTaOPjtdykoNdJ span{fill:#333;color:#333;}#mermaid-svg-43JTaOPjtdykoNdJ .node rect,#mermaid-svg-43JTaOPjtdykoNdJ .node circle,#mermaid-svg-43JTaOPjtdykoNdJ .node ellipse,#mermaid-svg-43JTaOPjtdykoNdJ .node polygon,#mermaid-svg-43JTaOPjtdykoNdJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-43JTaOPjtdykoNdJ .node .label{text-align:center;}#mermaid-svg-43JTaOPjtdykoNdJ .node.clickable{cursor:pointer;}#mermaid-svg-43JTaOPjtdykoNdJ .arrowheadPath{fill:#333333;}#mermaid-svg-43JTaOPjtdykoNdJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-43JTaOPjtdykoNdJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-43JTaOPjtdykoNdJ .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-43JTaOPjtdykoNdJ .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-43JTaOPjtdykoNdJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-43JTaOPjtdykoNdJ .cluster text{fill:#333;}#mermaid-svg-43JTaOPjtdykoNdJ .cluster span{color:#333;}#mermaid-svg-43JTaOPjtdykoNdJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-43JTaOPjtdykoNdJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建 设置 调用 触发 返回 QTcpServer Constructor Create QTcpServerPrivate object Set socketType as TcpSocket Call QTcpServerPrivate::configureCreatedSocket() qt_safe_poll Initialize socketEngine Set socketEngine as readNotification enabled SocketEngine's readNotification() Handle incoming connections Emit newConnection signal QTcpServer是QT网络模块中用于实现TCP服务器的类,其底层原理和技术细节包括: 基于操作系统的套接字(socket):QTcpServer利用操作系统提供的套接字接口来实现TCP通信,包括创建、绑定、监听和接受连接等操作。 事件循环机制:QTcpServer通常结合QT框架的事件循环机制使用,通过在事件循环中监听新连接事件,实现异步处理客户端的连接请求。 信号与槽机制:QTcpServer通过信号与槽机制实现对新连接的处理,当有新连接时触发相应的信号,可以连接到相应的槽函数进行处理。 多线程支持:QTcpServer可以在多线程环境下使用,通过QThread或QtConcurrent等机制,在新的线程中处理连接,并确保线程安全。 处理并发连接:QTcpServer能够处理并发的客户端连接,可以通过多路复用技术(如select或poll)来实现高效的并发处理。在Linux下可能采用select或者poll来实现输入输出复用。 处理网络错误:QTcpServer能够处理网络错误和异常情况,通过QAbstractSocket提供的错误处理机制,能够及时响应并处理网络异常,保证服务器的稳定性和可靠性。
香港云服务器租用推荐
服务器租用资讯
·广东云服务有限公司怎么样
·广东云服务器怎么样
·广东锐讯网络有限公司怎么样
·广东佛山的蜗牛怎么那么大
·广东单位电话主机号怎么填写
·管家婆 花生壳怎么用
·官网域名过期要怎么办
·官网邮箱一般怎么命名
·官网网站被篡改怎么办
服务器租用推荐
·美国服务器租用
·台湾服务器租用
·香港云服务器租用
·香港裸金属服务器
·香港高防服务器租用
·香港服务器租用特价