当前位置:首页>java>【深度分析】Qt网络编程核心:QTcpServer源码完全剖析,从原理到实战

【深度分析】Qt网络编程核心:QTcpServer源码完全剖析,从原理到实战

  • 2026-02-05 00:52:43
【深度分析】Qt网络编程核心:QTcpServer源码完全剖析,从原理到实战

【资源使用许可与免责协议】

重要提示:本协议是获取内容的法定前提。向下滚动或继续访问即构成您对本协议的有效签署,表示您已审慎阅读、理解并无条件接受全部条款,特别是加粗部分。

第一条 许可边界

1.1 您获得一项免费的、个人的、非商业的使用许可。

1.2 严禁任何商业用途(包括但不限于销售、集成、服务性使用)。

第二条 “现状”提供与不保证

2.1 内容按“现状”(AS-IS)且可能存在缺陷提供。

2.2 作者不作任何保证,且不提供任何形式的技术支持或服务

第三条 风险自担与责任上限

3.1 您确认技术实践存在固有风险,并自愿承担一切后果。

3.2 在法律允许的最大范围内,作者对任何间接、衍生或附带损失不承担责任

3.3 此限制不适用于因作者故意或重大过失直接造成的人身损害

第四条 您的法律责任

4.1 您保证使用行为完全合法

4.2 如因您违反本协议(特别是商业使用)导致作者遭受任何第三方索赔或损失,您须承担全部责任并足额赔偿

第五条 协议体系与更新

5.1 本协议与相关QQ群《群规》共同构成完整约定。您申请入群即构成对本协议的再次确认

5.2 协议更新后,您继续使用即视为接受。

欢迎来到【Qt开发宝典】微信公众号【知识宝库】!

本期《Qt C++工程师》动手实践系列,第037期。本期我们聚焦一个兼具实用性与学习深度的主题——《【深度分析】Qt网络编程核心:QTcpServer源码完全剖析,从原理到实战》。本系列文章旨在为关注Qt跨平台开发、Linux C++后端及系统底层架构的开发者,提供一个系统的学习参考。内容将通过原理讲解与实战结合的方式,解析相关核心知识与设计思路,以期对您的技术探索与实践有所裨益。

【技术交流说明】

欢迎关注【Qt开发宝典】微信公众号,定期分享更多Linux C++/Qt等技术干货!

技术交流QQ群:895876809(纯技术讨论群)。入群即构成您对本协议及群规的最终确认。

你是否曾经好奇过?Qt的QTcpServer是如何在单线程中优雅地处理成千上万的并发连接?为什么它能够如此高效地管理网络连接,而你的服务器代码却总是遇到性能瓶颈?

今天,我将深入Qt源码,逐行解析QTcpServer的实现机制。通过这次源码级解析,你将学到:

  • 事件驱动架构如何实现高效的异步I/O

  • PIMPL设计模式如何保持二进制兼容性

  • 内存管理的最佳实践和常见陷阱

  • 从listen()到readNotification()的完整执行流程

  • 5个真实应用场景和最佳实践

无论你是Qt初学者想要理解网络编程的本质,还是资深开发者希望学习优秀的架构设计,这篇文章都将为你提供深入的源码级解析和实战应用指导。

现在就QTcpSocket类源码分析如下(具体源码目录看图即可找到):

一:QTcpServer基石

1.QTcpServer概述

QTcpServer是Qt框架提供的TCP服务器类,继承自QObject,支持Qt的信号槽机制。它提供了完整的TCP服务器功能,包括监听连接、接受客户端、管理连接队列等。QTcpServer采用事件驱动的异步I/O模型,非常适合构建高性能的网络服务器应用。

为什么需要深入理解源码

  • 理解设计思想: 了解Qt网络编程的设计模式和架构

  • 优化性能: 理解内部机制有助于编写高效的网络应用

  • 扩展功能: 通过继承和重写实现自定义服务器(如SSL服务器)

  • 问题排查: 深入理解有助于快速定位和解决网络问题

2.类结构分析

2.1 继承关系

QObject  └── QTcpServer

QTcpServer继承自QObject,这意味着:

  • 支持Qt的信号槽机制

  • 支持对象树管理(自动删除子对象)

  • 支持事件循环和异步操作

2.2 核心组件

QTcpServer采用PIMPL(Pointer to Implementation)设计模式,将实现细节隐藏在QTcpServerPrivate类中:

classQTcpServer : publicQObject{    Q_OBJECT    // ...private:    Q_DECLARE_PRIVATE(QTcpServer)  // PIMPL模式};

PIMPL模式的优势

- 隐藏实现细节,保持二进制兼容性

- 减少头文件依赖,加快编译速度

- 便于修改实现而不影响公共接口

2.3 关键成员

公共接口

- 生命周期管理:listen()close()isListening()

- 连接管理:nextPendingConnection()hasPendingConnections()

- 状态查询:serverAddress()serverPort(), socketDescriptor()

- 错误处理:serverError()errorString()

- 连接控制:pauseAccepting()resumeAccepting()

受保护接口(供子类使用):

- incomingConnection(): 虚函数,处理新连接

- addPendingConnection(): 添加连接到队列

信号

newConnection(): 有新连接时发出

pendingConnectionAvailable(): 连接添

3. 核心实现机制

3.1 服务器启动流程

listen()方法是服务器启动的核心,其实现流程如下:

bool QTcpServer::listen(const QHostAddress &address, quint16 port){    // 1. 状态检查    if(d->state == QAbstractSocket::ListeningState) {        qWarning("QTcpServer::listen() called when already listening");        return false;    }    // 2. 解析网络协议类型    QAbstractSocket::NetworkLayerProtocol proto = address.protocol();    // 3. 解析网络代理设置    QNetworkProxy proxy = d->resolveProxy(addr, port);    // 4. 创建套接字引擎    d->socketEngine = QAbstractSocketEngine::createSocketEngine(...);    // 5. 初始化套接字引擎    d->socketEngine->initialize(d->socketType, proto);    // 6. 配置套接字选项(如Unix的SO_REUSEADDR)    d->configureCreatedSocket();    // 7. 绑定地址和端口    d->socketEngine->bind(addr, port);    // 8. 开始监听(设置积压队列)    d->socketEngine->listen(d->listenBacklog);    // 9. 设置事件接收器和启用读通知    d->socketEngine->setReceiver(d);    d->socketEngine->setReadNotificationEnabled(true);    // 10. 更新状态    d->state = QAbstractSocket::ListeningState;    d->address = d->socketEngine->localAddress();    d->port = d->socketEngine->localPort();    return true;}

关键点分析

1.套接字引擎抽象: 使用QAbstractSocketEngine抽象底层套接字操作,实现跨平台兼容

2.代理支持: 通过resolveProxy()解析代理设置,支持SOCKS等代理协议

3.平台特定处理configureCreatedSocket()在Unix平台设置SO_REUSEADDR,允许TIME_WAIT状态下重新绑定

4.事件驱动setReadNotificationEnabled(true)启用读通知,当有新连接时触发回调

3.2 连接接受机制

连接接受的核心是readNotification()方法,这是一个事件驱动的回调函数:

void QTcpServerPrivate::readNotification(){    Q_Q(QTcpServer);    // 无限循环处理所有待接受的连接(边缘触发模式)    for (;;) {        // 1. 检查连接数限制        if (totalPendingConnections() >= maxConnections) {            socketEngine->setReadNotificationEnabled(false);            return;        }        // 2. 接受新连接        qintptr descriptor = socketEngine->accept();        if (descriptor == -1) {            // 错误处理            if (socketEngine->error() != QAbstractSocket::TemporaryError) {                q->pauseAccepting();                emit q->acceptError(serverSocketError);            }            break;        }        // 3. 使用QPointer确保对象安全        QPointer<QTcpServer> that = q;        // 4. 调用虚函数处理新连接        q->incomingConnection(descriptor);        // 5. 发出信号        if (that)            emit q->newConnection();        // 6. 检查服务器状态        if (!that || !q->isListening())            return;    }}

设计亮点

1.边缘触发模式: 使用无限循环处理多个同时到达的连接,提高效率

2.连接数限制: 当达到maxPendingConnections时暂停接受,防止资源耗尽

3.对象安全: 使用QPointer确保在回调过程中服务器对象未被删除

4.错误分类: 区分临时错误和永久错误,临时错误可以重试

3.3 连接处理流程

默认的incomingConnection()实现:

void QTcpServer::incomingConnection(qintptr socketDescriptor){    // 1. 创建QTcpSocket对象    QTcpSocket *socket = new QTcpSocket(this);    // 2. 设置套接字描述符    socket->setSocketDescriptor(socketDescriptor);    // 3. 添加到待处理队列    addPendingConnection(socket);}

扩展性: 这是虚函数,子类可以重写以:

- 创建自定义套接字类型(如QSslServer创建QSslSocket

- 执行连接前验证

- 将连接传递给其他线程处理

4. 设计模式和技术特点

4.1 PIMPL模式

实现方式

classQTcpServer{private:    Q_DECLARE_PRIVATE(QTcpServer)  // 声明私有数据指针};// 使用Q_D宏访问私有数据#define Q_D(Class) Class##Private * const d = d_func()

优势

- 保持ABI兼容性:修改实现不影响二进制接口

- 减少编译依赖:头文件不暴露实现细节

- 加快编译速度:减少重编译范围

4.2 事件驱动架构

QTcpServer基于Qt事件循环实现异步I/O:

客户端连接    ↓操作系统完成三次握手    ↓socketEngine检测到读事件    ↓触发readNotification()回调    ↓调用incomingConnection()    ↓发出newConnection()信号    ↓应用程序槽函数处理

优势

- 非阻塞:不会阻塞事件循环

- 高效:单线程处理多个连接

- 集成:与Qt GUI应用无缝集成

4.3 虚函数设计

关键虚函数:

-incomingConnection(): 处理新连接

-hasPendingConnections(): 检查待处理连接

-nextPendingConnection(): 获取待处理连接

扩展示例(QSslServer):

classQSslServer : publicQTcpServer{protected:    void incomingConnection(qintptr socketDescriptor) override {        QSslSocket *socket = new QSslSocket(this);        socket->setSocketDescriptor(socketDescriptor);        socket->startServerEncryption();  // 启动SSL握手        addPendingConnection(socket);    }};
4.4 内存管理策略

对象树管理

-socketEngine作为子对象,自动删除

-QTcpSocket对象作为子对象,但建议显式删除

QPointer使用

QPointer<QTcpServer> that = q;q->incomingConnection(descriptor);if (that)  // 检查对象是否仍然存在    emit q->newConnection();

资源清理

void QTcpServer::close(){    qDeleteAll(d->pendingConnections);  // 删除所有待处理连接    d->pendingConnections.clear();    if (d->socketEngine) {        d->socketEngine->close();        d->socketEngine->deleteLater();  // 延迟删除        d->socketEngine = nullptr;    }}

5. 关键方法详解

5.1 listen() - 服务器启动

功能: 开始监听指定地址和端口的连接

参数:

-address: IP地址(默认QHostAddress::Any,监听所有接口)

-port: 端口号(默认0,自动选择)

返回值: 成功返回true,失败返回false

错误处理: 通过serverError()errorString()查询错误

使用示例:

QTcpServer *server = new QTcpServer(this);if (!server->listen(QHostAddress::Any, 8080)) {    qDebug() << "监听失败:" << server->errorString();    return;}qDebug() << "服务器监听在端口:" << server->serverPort();

5.2 nextPendingConnection() - 获取连接

功能: 从待处理队列中取出下一个连接

返回值: QTcpSocket指针,没有连接时返回nullptr

所有权: 返回的套接字所有权转移给调用者,必须手动删除

线程安全: 返回的套接字不能跨线程使用

使用示例:

void MyServer::handleNewConnection(){    QTcpSocket *socket = server->nextPendingConnection();    if (socket) {        connect(socket, &QTcpSocket::readyRead,                 this, &MyServer::readData);        connect(socket, &QTcpSocket::disconnected,                 socket, &QTcpSocket::deleteLater);    }}

5.3 waitForNewConnection() - 阻塞等待

功能: 阻塞等待新连接(用于没有事件循环的场景)

参数:

-msec: 超时时间(毫秒),-1表示不超时

-timedOut: 可选参数,返回是否超时

使用场景: 命令行工具、后台服务、单线程服务器

注意事项: 在GUI应用中不推荐使用,会冻结界面

使用示例:

QTcpServer server;server.listen(QHostAddress::Any8080);while(true) {    bool timedOut = false;    if(server.waitForNewConnection(1000, &timedOut)) {        QTcpSocket *socket = server.nextPendingConnection();        // 处理连接        processConnection(socket);        socket->deleteLater();    } else if (timedOut) {        // 超时处理        continue;    } else {        // 错误处理        break;    }}

5.4 pauseAccepting() / resumeAccepting() - 连接控制

功能: 临时暂停/恢复接受新连接

使用场景:

- 服务器负载过高时暂停接受

- 进行维护操作时暂停服务

- 需要处理完现有连接后再接受新连接

实现原理: 通过setReadNotificationEnabled(false/true)控制

使用示例:

// 暂停接受server->pauseAccepting();// 处理现有连接processPendingConnections();// 恢复接受server->resumeAccepting();

6. 应用场景

6.1 Web服务器

场景: 构建HTTP/HTTPS服务器

实现要点:

- 使用QSslServer实现HTTPS

- 解析HTTP请求头

- 生成HTTP响应

示例架构:

class HttpServer : public QTcpServer {protected:    voidincomingConnection(qintptr socketDescriptor)override{        HttpConnection *connection = new HttpConnection(this);        connection->setSocketDescriptor(socketDescriptor);        // 处理HTTP请求    }};

6.2 游戏服务器

场景: 多人在线游戏服务器

实现要点:

- 处理大量并发连接

- 实现游戏协议

- 房间管理和匹配系统

优化建议:

- 使用线程池处理连接

- 合理设置maxPendingConnections

- 实现连接池复用

6.3 聊天服务器

场景: 即时通讯服务器

实现要点:

- 用户认证和会话管理

- 消息路由和转发

- 群组聊天支持

架构设计:

class ChatServer : public QTcpServer {    Q_OBJECTpublic:    ChatServer(QObject *parent = nullptr);private slots:    voidhandleNewConnection();    voidhandleClientData();    voidhandleClientDisconnected();private:    QMap<QString, QTcpSocket*> clients;  // 客户端映射};

6.4 API服务器

场景: RESTful API服务器

实现要点:

- 解析JSON请求

- 路由处理

- 认证和授权

示例:

class ApiServer : public QTcpServer {protected:    voidincomingConnection(qintptr socketDescriptor)override{        ApiConnection *conn = new ApiConnection(this);        conn->setSocketDescriptor(socketDescriptor);        connect(conn, &ApiConnection::requestReady,                 this, &ApiServer::handleRequest);    }};

6.5 文件传输服务器

场景: FTP服务器、文件同步服务器

实现要点:

- 文件上传/下载

- 断点续传

- 传输进度通知

7. 最佳实践

7.1 错误处理

必须检查listen()返回值:

if(!server->listen(QHostAddress::Any, port)) {    qCritical() << "服务器启动失败:" << server->errorString();    return false;}

监听错误信号:

connect(server, &QTcpServer::acceptError        this, [](QAbstractSocket::SocketError error) {    qWarning() << "接受连接错误:" << error;});

二:QTcpServer.h文件源码详细总结

qtcpserver.h是Qt网络模块中定义TCP服务器类的核心头文件。该类提供了创建和管理TCP服务器所需的所有功能,是构建网络服务器应用程序的基础组件。

类结构

类名:QTcpServer

继承关系:QTcpServer : public QObject

设计模式:-PIMPL(Pointer to Implementation):使用Q_DECLARE_PRIVATE隐藏实现细节

- 信号槽机制:继承自QObject,支持Qt的事件驱动编程

- 不可复制:使用Q_DISABLE_COPY禁止复制构造和赋值

// Copyright (C) 2016 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only/** * @file qtcpserver.h * @brief QTcpServer类的头文件定义 *  * 本文件定义了QTcpServer类,这是Qt框架中用于创建TCP服务器的基础类。 * QTcpServer提供了接受传入TCP连接的功能,是构建网络服务器应用程序的核心组件。 *  * 主要功能: * - 监听指定地址和端口的TCP连接 * - 接受客户端连接请求 * - 管理待处理连接队列 * - 提供错误处理和状态查询 * - 支持网络代理配置 *  * 使用场景: * - 网络服务器应用程序 * - 客户端-服务器通信架构 * - 需要处理多个并发连接的场景 */#ifndef QTCPSERVER_H#define QTCPSERVER_H#include <QtNetwork/qtnetworkglobal.h>  // Qt网络模块的全局定义#include <QtCore/qobject.h>              // QObject基类,提供信号槽机制#include <QtNetwork/qabstractsocket.h>   // 抽象套接字基类,定义套接字状态和错误类型#include <QtNetwork/qhostaddress.h>      // 主机地址类,用于表示IP地址QT_BEGIN_NAMESPACE// 前向声明:QTcpServer的私有实现类(使用PIMPL设计模式)classQTcpServerPrivate;// 条件编译:如果启用了网络代理功能,则前向声明QNetworkProxy类#ifndef QT_NO_NETWORKPROXYclassQNetworkProxy;#endif// 前向声明:TCP套接字类,用于与客户端进行通信classQTcpSocket;/** * @class QTcpServer * @brief TCP服务器类,用于接受和处理传入的TCP连接 *  * QTcpServer是Qt网络模块中用于创建TCP服务器的核心类。它继承自QObject, * 因此支持Qt的信号槽机制,可以方便地处理异步事件。 *  * 工作原理: * 1. 调用listen()方法开始监听指定地址和端口 * 2. 当有客户端连接时,触发newConnection()信号 * 3. 调用nextPendingConnection()获取待处理的连接 * 4. 使用返回的QTcpSocket对象与客户端进行数据通信 *  * 特性: * - 支持多连接:可以同时处理多个客户端连接 * - 事件驱动:基于Qt事件循环,支持异步操作 * - 线程安全:可以在多线程环境中使用 * - 可扩展:提供虚函数供子类重写以自定义行为 *  * @note 该类不可复制(Q_DISABLE_COPY),必须通过指针或引用传递 *  * @see QTcpSocket, QAbstractSocket */class Q_NETWORK_EXPORT QTcpServer : public QObject{    Q_OBJECT  // 启用Qt的元对象系统,支持信号槽机制public:    /**     * @brief 构造函数,创建一个QTcpServer对象     * @param parent 父对象指针,用于Qt对象树管理(可选,默认为nullptr)     *      * 创建一个新的TCP服务器实例。服务器创建后需要调用listen()方法     * 才能开始监听连接。     *      * @note 使用explicit关键字防止隐式类型转换     */    explicit QTcpServer(QObject *parent = nullptr);    /**     * @brief 虚析构函数     *      * 销毁QTcpServer对象。如果服务器正在监听连接,会自动调用close()关闭监听。     * 任何仍然连接的客户端QTcpSocket对象必须在服务器删除前断开连接或重新设置父对象。     */    virtual ~QTcpServer();    /**     * @brief 开始监听传入的连接     * @param address 要监听的IP地址(默认为QHostAddress::Any,监听所有网络接口)     * @param port 要监听的端口号(默认为0,表示自动选择一个可用端口)     * @return 成功返回true,失败返回false     *      * 使服务器开始监听指定地址和端口的传入TCP连接。     *      * 参数说明:     * - address:      *   - QHostAddress::Any: 监听所有可用的IPv4网络接口     *   - QHostAddress::AnyIPv6: 监听所有可用的IPv6网络接口     *   - 特定IP地址: 只监听指定IP地址的连接     * - port:     *   - 0: 系统自动选择一个可用端口(可通过serverPort()查询)     *   - 非0: 监听指定的端口号     *      * @note 如果服务器已经在监听,此方法会先关闭当前监听再重新开始     * @see isListening(), serverAddress(), serverPort()     */    bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);    /**     * @brief 关闭服务器,停止监听连接     *      * 关闭服务器套接字,停止接受新的连接。已建立的连接不会被关闭。     * 调用此方法后,isListening()将返回false。     *      * @see listen(), isListening()     */    void close();    /**     * @brief 检查服务器是否正在监听连接     * @return 如果正在监听返回true,否则返回false     *      * 返回服务器的当前监听状态。只有在成功调用listen()后才会返回true。     *      * @see listen(), close()     */    bool isListening() const;    /**     * @brief 设置最大待处理连接数     * @param numConnections 最大待处理连接数     *      * 设置服务器在等待调用nextPendingConnection()接受连接之前,     * 可以保持的最大待处理连接数。当达到此限制时,新的连接请求     * 可能会被拒绝或延迟处理。     *      * 默认值通常由操作系统决定,典型值为30     *      * @note 此设置影响的是待处理连接队列的大小,不是总连接数     * @see maxPendingConnections(), nextPendingConnection()     */    void setMaxPendingConnections(int numConnections);    /**     * @brief 获取最大待处理连接数     * @return 当前设置的最大待处理连接数     *      * @see setMaxPendingConnections()     */    int maxPendingConnections() const;    /**     * @brief 设置监听积压队列大小     * @param size 积压队列大小     *      * 设置底层套接字监听积压队列的大小。这个队列存储了已完成三次握手     * 但尚未被accept()接受的连接。     *      * 较大的值可以处理更多的并发连接请求,但会消耗更多系统资源。     *      * @note 实际生效的大小可能受操作系统限制     * @see listenBacklogSize()     */    void setListenBacklogSize(int size);    /**     * @brief 获取监听积压队列大小     * @return 当前设置的积压队列大小     *      * @see setListenBacklogSize()     */    int listenBacklogSize() const;    /**     * @brief 获取服务器正在监听的端口号     * @return 端口号,如果未在监听则返回0     *      * 返回服务器当前监听的端口号。如果listen()时指定port为0(自动选择),     * 此方法返回系统实际分配的端口号。     *      * @see serverAddress(), listen()     */    quint16 serverPort() const;    /**     * @brief 获取服务器正在监听的IP地址     * @return 服务器地址,如果未在监听则返回QHostAddress::Null     *      * 返回服务器当前监听的IP地址。如果listen()时指定address为QHostAddress::Any,     * 此方法返回QHostAddress::Any。     *      * @see serverPort(), listen()     */    QHostAddress serverAddress() const;    /**     * @brief 获取服务器套接字描述符     * @return 套接字描述符,如果未在监听则返回-1     *      * 返回底层服务器套接字的原生套接字描述符。这个描述符可以用于     * 平台特定的操作或传递给其他API。     *      * @note 在Windows上返回的是SOCKET类型,在Unix系统上返回的是int类型     * @see setSocketDescriptor()     */    qintptr socketDescriptor() const;    /**     * @brief 使用现有的套接字描述符初始化服务器     * @param socketDescriptor 已存在的套接字描述符     * @return 成功返回true,失败返回false     *      * 使用一个已经创建并绑定好的套接字描述符来初始化服务器。     * 这在需要与现有代码集成或使用特定套接字选项时很有用。     *      * @note 套接字必须已经绑定到地址和端口,并且处于监听状态     * @see socketDescriptor()     */    bool setSocketDescriptor(qintptr socketDescriptor);    /**     * @brief 等待新连接(阻塞方式)     * @param msec 超时时间(毫秒),0表示不超时,-1表示使用默认超时     * @param timedOut 可选参数,如果连接超时则设置为true     * @return 如果有新连接返回true,超时或出错返回false     *      * 阻塞等待直到有新连接可用或超时。这个方法主要用于不使用事件循环的场景。     *      * 使用场景:     * - 单线程服务器应用     * - 需要同步处理连接的场景     *      * @note 在有事件循环的情况下,应该使用newConnection()信号而不是此方法     * @see newConnection(), hasPendingConnections()     */    bool waitForNewConnection(int msec = 0, bool *timedOut = nullptr);    /**     * @brief 检查是否有待处理的连接     * @return 如果有待处理连接返回true,否则返回false     *      * 检查是否有已建立但尚未通过nextPendingConnection()接受的连接。     *      * @note 这是一个虚函数,子类可以重写以自定义行为     * @see nextPendingConnection(), newConnection()     */    virtual bool hasPendingConnections() const;    /**     * @brief 获取下一个待处理的连接     * @return 指向QTcpSocket对象的指针,如果没有待处理连接则返回nullptr     *      * 从待处理连接队列中取出下一个连接,返回一个已连接的QTcpSocket对象。     * 调用者负责删除返回的QTcpSocket对象。     *      * 返回的套接字处于QAbstractSocket::ConnectedState状态,可以直接使用     * 进行读写操作。     *      * @note 这是一个虚函数,子类可以重写以返回自定义的套接字类型(如QSslSocket)     * @note 返回的套接字对象的所有权转移给调用者,必须手动删除     * @see hasPendingConnections(), newConnection(), addPendingConnection()     */    virtual QTcpSocket *nextPendingConnection();    /**     * @brief 获取最后一次发生的服务器错误     * @return 错误类型枚举值     *      * 返回服务器最后一次发生的错误类型。如果没有错误,返回QAbstractSocket::UnknownSocketError。     *      * @see errorString(), QAbstractSocket::SocketError     */    QAbstractSocket::SocketError serverError() const;    /**     * @brief 获取最后一次错误的可读描述     * @return 错误描述字符串     *      * 返回一个人类可读的错误描述字符串,说明最后一次发生的错误。     *      * @see serverError()     */    QString errorString() const;    /**     * @brief 暂停接受新连接     *      * 临时暂停接受新的连接请求。已建立的连接不受影响。     * 调用resumeAccepting()可以恢复接受连接。     *      * 使用场景:     * - 服务器负载过高时临时停止接受新连接     * - 进行维护操作时暂停服务     *      * @see resumeAccepting()     */    void pauseAccepting();    /**     * @brief 恢复接受新连接     *      * 恢复接受新的连接请求。只有在之前调用了pauseAccepting()时才需要调用此方法。     *      * @see pauseAccepting()     */    void resumeAccepting();    // 条件编译:网络代理相关功能#ifndef QT_NO_NETWORKPROXY    /**     * @brief 设置网络代理     * @param networkProxy 网络代理配置对象     *      * 为服务器设置网络代理。代理设置会影响服务器如何建立连接。     *      * @note 必须在调用listen()之前设置代理     * @see proxy(), QNetworkProxy     */    void setProxy(const QNetworkProxy &networkProxy);    /**     * @brief 获取当前网络代理配置     * @return 当前设置的网络代理对象     *      * @see setProxy()     */    QNetworkProxy proxy() const;#endifprotected:    /**     * @brief 处理新连接(受保护的虚函数)     * @param handle 新连接的套接字描述符     *      * 当有新连接到达时,框架会调用此方法。默认实现创建一个QTcpSocket对象     * 并将其添加到待处理连接队列。     *      * 子类可以重写此方法以:     * - 创建自定义的套接字类型(如QSslSocket)     * - 执行连接前的验证或初始化     * - 实现自定义的连接处理逻辑     *      * @note 子类重写时通常应该调用addPendingConnection()将套接字添加到队列     * @see addPendingConnection(), nextPendingConnection()     */    virtual void incomingConnection(qintptr handle);    /**     * @brief 添加套接字到待处理连接队列     * @param socket 要添加的QTcpSocket对象指针     *      * 将已连接的套接字添加到待处理连接队列。通常在重写incomingConnection()时调用。     *      * @note 套接字必须已经处于连接状态     * @note 此方法会触发pendingConnectionAvailable()信号     * @see incomingConnection(), nextPendingConnection()     */    void addPendingConnection(QTcpSocket* socket);    /**     * @brief 受保护的构造函数(用于子类)     * @param socketType 套接字类型(TCP、UDP等)     * @param dd 私有数据对象的引用(用于PIMPL模式)     * @param parent 父对象指针     *      * 这是一个受保护的构造函数,允许子类(如QSslServer)创建自定义类型的服务器。     * 使用PIMPL(Pointer to Implementation)设计模式来管理私有数据。     *      * @note 普通用户不应直接使用此构造函数     */    QTcpServer(QAbstractSocket::SocketType socketType, QTcpServerPrivate &dd,               QObject *parent = nullptr);Q_SIGNALS:    /**     * @brief 新连接信号     *      * 当有新连接可用时发出此信号。每次有客户端连接到服务器时都会触发。     *      * 使用方式:     * @code     * connect(server, &QTcpServer::newConnection, this, &MyClass::handleNewConnection);     * @endcode     *      * @note 此信号在有新连接时立即发出,即使连接尚未被添加到待处理队列     * @see hasPendingConnections(), nextPendingConnection(), pendingConnectionAvailable()     */    void newConnection();    /**     * @brief 待处理连接可用信号     *      * 当连接被添加到待处理连接队列时发出此信号。这与newConnection()的区别是:     * - newConnection(): 连接建立时立即发出     * - pendingConnectionAvailable(): 连接被添加到队列后发出     *      * @note 这是一个私有信号(QPrivateSignal),主要用于内部使用     * @see newConnection(), addPendingConnection()     */    void pendingConnectionAvailable(QPrivateSignal);    /**     * @brief 接受连接错误信号     * @param socketError 发生的错误类型     *      * 当在accept()操作过程中发生错误时发出此信号。可以用于错误处理和日志记录。     *      * @see serverError(), errorString()     */    void acceptError(QAbstractSocket::SocketError socketError);private:    // 禁止复制构造和赋值操作(Qt对象通常不可复制)    Q_DISABLE_COPY(QTcpServer)    // 声明私有数据指针(PIMPL设计模式)    Q_DECLARE_PRIVATE(QTcpServer)};QT_END_NAMESPACE#endif // QTCPSERVER_H

三:QTcpServer.cpp文件源码详细总结

qtcpserver.cpp是Qt网络模块中QTcpServer类的实现文件。该文件包含了QTcpServer类的所有功能实现,从服务器的创建、监听、连接到错误处理,提供了完整的TCP服务器功能。

文件结构

1. 文件头部

- 版权信息和许可证声明

- 文件功能说明注释

- 调试宏定义(QTCPSERVER_DEBUG)

2. Qt文档注释

- 类的详细文档(使用Qt文档系统格式)

- 信号的文档说明

3. 头文件包含

-qtcpserver.h: 公共接口声明

-qtcpserver_p.h: 私有实现类

- 各种Qt核心和网络模块头文件

4. 实现部分

- QTcpServerPrivate类的实现

- QTcpServer类的实现

- 各种辅助函数和宏定义

// Copyright (C) 2016 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only/** * @file qtcpserver.cpp * @brief QTcpServer类的实现文件 *  * 本文件实现了QTcpServer类的所有功能,包括: * - 服务器的创建、监听和关闭 * - 连接接受和处理 * - 错误处理和状态管理 * - 网络代理支持 * - 平台特定的套接字操作 *  * 核心设计: * - 使用PIMPL模式隐藏实现细节(QTcpServerPrivate) * - 使用QAbstractSocketEngine进行底层套接字操作 * - 基于Qt事件循环的异步I/O处理 * - 支持跨平台网络编程 */// 调试宏定义:取消注释以启用调试输出//#define QTCPSERVER_DEBUG/*! \class QTcpServer    \brief The QTcpServer class provides a TCP-based server.    \reentrant    \ingroup network    \inmodule QtNetwork    This class makes it possible to accept incoming TCP connections.    You can specify the port or have QTcpServer pick one    automatically. You can listen on a specific address or on all the    machine's addresses.    Call listen() to have the server listen for incoming connections.    The newConnection() signal is then emitted each time a client    connects to the server. When the client connection has been added    to the pending connection queue using the addPendingConnection()    function, the pendingConnectionAvailable() signal is emitted.    Call nextPendingConnection() to accept the pending connection as    a connected QTcpSocket. The function returns a pointer to a    QTcpSocket in QAbstractSocket::ConnectedState that you can use for    communicating with the client.    If an error occurs, serverError() returns the type of error, and    errorString() can be called to get a human readable description of    what happened.    When listening for connections, the address and port on which the    server is listening are available as serverAddress() and    serverPort().    Calling close() makes QTcpServer stop listening for incoming    connections.    Although QTcpServer is mostly designed for use with an event    loop, it's possible to use it without one. In that case, you must    use waitForNewConnection(), which blocks until either a    connection is available or a timeout expires.    \sa QTcpSocket, {Fortune Server}, {Threaded Fortune Server},        {Torrent Example}*//*! \fn void QTcpServer::newConnection()    This signal is emitted every time a new connection is available, regardless    of whether it has been added to the pending connections queue or not.    \sa hasPendingConnections(), nextPendingConnection()*//*! \fn void QTcpServer::pendingConnectionAvailable()    This signal is emitted every time a new connection has been added to the    pending connections queue.    \sa hasPendingConnections(), nextPendingConnection()    \since 6.4*//*! \fn void QTcpServer::acceptError(QAbstractSocket::SocketError socketError)    \since 5.0    This signal is emitted when accepting a new connection results in an error.    The \a socketError parameter describes the type of error that occurred.    \sa pauseAccepting(), resumeAccepting()*/#include "qtcpserver.h"                    // QTcpServer类声明#include "qtcpserver_p.h"                  // QTcpServerPrivate私有实现类#include "qalgorithms.h"                   // Qt算法工具函数#include "qhostaddress.h"                  // IP地址类#include "qlist.h"                         // Qt列表容器#include "qpointer.h"                      // 弱指针,用于安全的对象引用#include "qabstractsocketengine_p.h"        // 抽象套接字引擎(私有API)#include "qtcpsocket.h"                    // TCP套接字类#include "qnetworkproxy.h"                 // 网络代理类QT_BEGIN_NAMESPACE/** * @brief 套接字引擎检查宏 * @param returnValue 如果套接字引擎不存在时返回的值 *  * 用于在访问套接字引擎之前检查其是否存在,避免空指针访问。 * 如果套接字引擎未初始化,立即返回指定的值。 */#define Q_CHECK_SOCKETENGINE(returnValue) do { \    if(!d->socketEngine) { \        return returnValue; \    } } while(0)/** * @brief QTcpServerPrivate构造函数 * @internal *  * 初始化QTcpServer的私有数据成员: * - port: 监听端口,初始化为0(未绑定) * - socketType: 套接字类型,初始化为UnknownSocketType * - state: 套接字状态,初始化为UnconnectedState(未连接) * - socketEngine: 套接字引擎指针,初始化为nullptr * - serverSocketError: 服务器错误,初始化为UnknownSocketError * - maxConnections: 最大待处理连接数,默认30个 *  * 这些成员变量在服务器开始监听时会被更新。 */QTcpServerPrivate::QTcpServerPrivate() : port(0)                                                      // 端口号初始为0 , socketType(QAbstractSocket::UnknownSocketType)              // 套接字类型未知 , state(QAbstractSocket::UnconnectedState)                    // 状态为未连接 , socketEngine(nullptr)                                       // 套接字引擎为空 , serverSocketError(QAbstractSocket::UnknownSocketError)      // 无错误 , maxConnections(30)                                          // 默认最大30个待处理连接{}/** * @brief QTcpServerPrivate析构函数 * @internal *  * 清理QTcpServerPrivate的资源。注意: * - socketEngine的生命周期由QTcpServer管理(作为子对象) * - pendingConnections中的套接字会在QTcpServer::close()中删除 * - 这里主要是清理其他资源(如果有的话) */QTcpServerPrivate::~QTcpServerPrivate(){}#ifndef QT_NO_NETWORKPROXY/** * @brief 解析并确定最终使用的网络代理 * @internal * @param address 要连接的目标地址 * @param port 要连接的目标端口 * @return 解析后的网络代理对象 *  * 根据以下规则解析代理: * 1. 如果地址是回环地址(localhost),不使用代理 * 2. 如果已通过setProxy()设置了非默认代理,使用该代理 * 3. 否则,查询应用程序的代理设置(QNetworkProxyFactory) * 4. 检查代理是否支持服务器监听功能 * 5. 返回第一个可用的代理,如果没有则返回DefaultProxy(会触发错误) *  * 代理能力检查: * - TCP套接字需要ListeningCapability能力 * - SCTP套接字需要SctpListeningCapability能力 */QNetworkProxy QTcpServerPrivate::resolveProxy(const QHostAddress &address, quint16 port){    // 回环地址不需要代理    if(address.isLoopback())        return QNetworkProxy::NoProxy;    QList<QNetworkProxy> proxies;    if(proxy.type() != QNetworkProxy::DefaultProxy) {        // 如果通过setProxy()设置了非默认代理,使用该代理        proxies << proxy;    } else {        // 否则查询应用程序的代理设置        QNetworkProxyQuery query(port, QString(),                                 socketType == QAbstractSocket::SctpSocket ?                                 QNetworkProxyQuery::SctpServer:      // SCTP服务器查询                                 QNetworkProxyQuery::TcpServer);      // TCP服务器查询        proxies = QNetworkProxyFactory::proxyForQuery(query);    }    // 返回第一个支持监听功能的代理    for(const QNetworkProxy &p : std::as_const(proxies)) {        // TCP套接字需要监听能力        if(socketType == QAbstractSocket::TcpSocket &&            (p.capabilities() & QNetworkProxy::ListeningCapability) != 0)            return p;        // SCTP套接字需要SCTP监听能力        if(socketType == QAbstractSocket::SctpSocket &&            (p.capabilities() & QNetworkProxy::SctpListeningCapability) != 0)            return p;    }    // 没有找到可用的代理    // DefaultProxy会在后续操作中触发错误    return QNetworkProxy(QNetworkProxy::DefaultProxy);}#endif/** * @brief 配置新创建的套接字 * @internal *  * 为新创建的套接字设置平台特定的选项。 *  * Unix平台: * - 设置SO_REUSEADDR选项,允许在TIME_WAIT状态下重新绑定端口 * - 这对于服务器重启很重要,避免"Address already in use"错误 * - 即使设置失败也不中止(某些引擎如SOCKS可能不支持此选项) *  * Windows平台: * - 不需要此设置,Windows默认允许重新绑定 * - Windows上SO_REUSEADDR的含义不同(允许多个监听套接字共享地址端口) *  * @note 此方法在listen()过程中调用,用于配置底层套接字 */void QTcpServerPrivate::configureCreatedSocket(){#if defined(Q_OS_UNIX)    // Unix平台:设置地址重用选项    // 允许在TIME_WAIT状态下重新绑定端口,这对服务器重启很重要    // 即使设置失败也不中止,因为某些引擎(如SOCKS)可能不支持此选项    socketEngine->setOption(QAbstractSocketEngine::AddressReusable1);#endif    // Windows平台不需要此设置,默认行为已满足需求}/** * @brief 处理读通知事件(接受新连接) * @internal *  * 当套接字引擎检测到有新连接到达时,会调用此方法。 * 这是一个事件驱动的回调函数,由QAbstractSocketEngine触发。 *  * 处理流程: * 1. 检查待处理连接数是否达到上限,如果是则暂停接受 * 2. 调用accept()接受新连接,获取套接字描述符 * 3. 如果accept失败: *    - 临时错误:继续循环,等待下次重试 *    - 永久错误:暂停接受,记录错误,发出acceptError信号 * 4. 如果accept成功: *    - 调用incomingConnection()处理新连接(创建QTcpSocket) *    - 发出newConnection()信号通知应用程序 *    - 检查服务器是否仍在监听,如果不是则返回 *  * 使用QPointer确保在回调过程中服务器对象未被删除。 *  * @note 此方法在事件循环中被调用,是异步的 * @note 使用无限循环处理多个同时到达的连接(边缘触发模式) */void QTcpServerPrivate::readNotification(){    Q_Q(QTcpServer);  // 获取公共接口指针    // 无限循环处理所有待接受的连接(边缘触发模式)    for(;;) {        // 检查待处理连接数是否达到上限        if(totalPendingConnections() >= maxConnections) {#if defined (QTCPSERVER_DEBUG)            qDebug("QTcpServerPrivate::_q_processIncomingConnection() too many connections");#endif            // 达到上限,暂停接受新连接            if(socketEngine->isReadNotificationEnabled())                socketEngine->setReadNotificationEnabled(false);            return;        }        // 接受新连接,获取套接字描述符        qintptr descriptor = socketEngine->accept();        if(descriptor == -1) {            // accept失败            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        // 使用QPointer确保在回调过程中服务器对象未被删除        QPointer<QTcpServer> that = q;        // 调用虚函数处理新连接(创建QTcpSocket并添加到队列)        q->incomingConnection(descriptor);        // 如果服务器对象仍然存在,发出新连接信号        if(that)            emit q->newConnection();        // 如果服务器对象已被删除或不再监听,退出循环        if(!that || !q->isListening())            return;    }}/** * @brief 获取当前待处理连接队列中的套接字数量 * @internal * @return 待处理连接的数量 *  * 返回当前在待处理连接队列中的套接字数量。 * 这个方法是为了让maxPendingConnections功能能够正确工作, * 特别是对于那些在连接建立时不一定立即有"就绪"套接字的服务器, * 例如QSslServer(需要完成SSL握手后才能使用)。 *  * 默认实现直接返回pendingConnections.size(),这与之前的行为一致。 * 子类可以重写此方法以实现自定义的计数逻辑。 *  * @note 此方法在readNotification()中被调用来检查连接数限制 * @see maxPendingConnections, setMaxPendingConnections */int QTcpServerPrivate::totalPendingConnections() const{    return int(pendingConnections.size());}/** * @brief QTcpServer构造函数 *  * 创建一个新的QTcpServer对象。 *  * @param parent 父对象指针,用于Qt对象树管理(可选) *  * 初始化过程: * 1. 创建QTcpServerPrivate私有数据对象(使用PIMPL模式) * 2. 将私有数据对象作为子对象传递给QObject(自动管理生命周期) * 3. 设置套接字类型为TCP套接字 *  * 创建后需要调用listen()方法才能开始监听连接。 *  * @note 使用explicit关键字防止隐式类型转换 * @see listen(), setSocketDescriptor() */QTcpServer::QTcpServer(QObject *parent)    : QObject(*new QTcpServerPrivate, parent)  // 创建私有数据对象并传递给QObject{    Q_D(QTcpServer);  // 获取私有数据指针的宏#if defined(QTCPSERVER_DEBUG)    qDebug("QTcpServer::QTcpServer(%p)"parent);#endif    d->socketType = QAbstractSocket::TcpSocket;  // 设置为TCP套接字类型}/** * @brief QTcpServer析构函数 *  * 销毁QTcpServer对象。如果服务器正在监听连接,会自动调用close()关闭监听。 *  * 清理过程: * 1. 调用close()关闭服务器套接字 * 2. 删除所有待处理的连接 * 3. 清理套接字引擎 * 4. QObject析构函数会自动删除私有数据对象(因为是子对象) *  * 重要提示: * - 任何仍然连接的客户端QTcpSocket对象必须在服务器删除前: *   - 断开连接,或 *   - 重新设置父对象(reparent) * - 否则这些套接字也会被删除,可能导致未完成的操作被中断 *  * @see close() */QTcpServer::~QTcpServer(){#if defined(QTCPSERVER_DEBUG)    qDebug("QTcpServer::~QTcpServer()");#endif    close();  // 关闭服务器,清理资源}/** * @brief 受保护的构造函数(用于子类) * @internal *  * 这是一个受保护的构造函数,允许子类(如QSslServer)创建自定义类型的服务器。 *  * @param socketType 套接字类型(TCP、UDP、SCTP等) * @param dd 私有数据对象的引用(用于PIMPL模式) * @param parent 父对象指针 *  * 使用场景: * - QSslServer继承QTcpServer,需要创建SSL服务器 * - 其他需要自定义套接字类型的服务器实现 *  * 与公共构造函数的区别: * - 使用传入的私有数据对象引用,而不是创建新的 * - 允许设置自定义的套接字类型 *  * @note 普通用户不应直接使用此构造函数 * @note 子类必须提供自己的私有数据类(继承自QTcpServerPrivate) */QTcpServer::QTcpServer(QAbstractSocket::SocketType socketType, QTcpServerPrivate &dd,                       QObject *parent) : QObject(dd, parent)  // 使用传入的私有数据对象{    Q_D(QTcpServer);#if defined(QTCPSERVER_DEBUG)    qDebug("QTcpServer::QTcpServer(%sSocket, QTcpServerPrivate == %p, parent == %p)",           socketType == QAbstractSocket::TcpSocket ? "Tcp"          : socketType == QAbstractSocket::UdpSocket ? "Udp"          : socketType == QAbstractSocket::SctpSocket ? "Sctp"          : "Unknown", &dd, parent);#endif    d->socketType = socketType;  // 设置套接字类型}/** * @brief 开始监听传入的连接 *  * 使服务器开始监听指定地址和端口的传入TCP连接。 *  * @param address 要监听的IP地址(默认为QHostAddress::Any,监听所有网络接口) * @param port 要监听的端口号(默认为0,表示自动选择一个可用端口) * @return 成功返回true,失败返回false *  * 执行流程: * 1. 检查是否已在监听状态,如果是则返回false * 2. 解析网络协议类型(IPv4/IPv6) * 3. 解析网络代理设置 * 4. 创建并初始化套接字引擎 * 5. 配置套接字选项(如地址重用) * 6. 绑定地址和端口 * 7. 开始监听连接 * 8. 设置事件接收器和读通知 * 9. 更新服务器状态和地址端口信息 *  * 错误处理: * - 如果任何步骤失败,会设置serverSocketError和serverSocketErrorString * - 可以通过serverError()和errorString()查询错误信息 *  * @note 如果服务器已经在监听,此方法会先关闭当前监听再重新开始 * @note 如果port为0,系统会自动选择一个可用端口,可通过serverPort()查询 * @see isListening(), serverAddress(), serverPort(), close() */bool QTcpServer::listen(const QHostAddress &address, quint16 port){    Q_D(QTcpServer);    // 检查是否已在监听状态    if(d->state == QAbstractSocket::ListeningState) {        qWarning("QTcpServer::listen() called when already listening");        return false;    }    // 获取地址的网络协议类型(IPv4/IPv6)    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    // 删除旧的套接字引擎(如果存在)    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;    }    // 初始化套接字引擎    if(!d->socketEngine->initialize(d->socketType, proto)) {        // 初始化失败:记录错误信息        d->serverSocketError = d->socketEngine->error();        d->serverSocketErrorString = d->socketEngine->errorString();        return false;    }    // 获取实际使用的协议类型(可能从AnyIPProtocol变为IPv4Protocol)    proto = d->socketEngine->protocol();    if(addr.protocol() == QAbstractSocket::AnyIPProtocol && proto == QAbstractSocket::IPv4Protocol)        addr = QHostAddress::AnyIPv4;  // 统一使用IPv4地址    // 配置套接字选项(如Unix平台的地址重用)    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->listenBacklog)) {        // 监听失败:记录错误信息        d->serverSocketError = d->socketEngine->error();        d->serverSocketErrorString = d->socketEngine->errorString();        return false;    }    // 设置事件接收器(用于接收readNotification回调)    d->socketEngine->setReceiver(d);    // 启用读通知(当有新连接到达时触发readNotification)    d->socketEngine->setReadNotificationEnabled(true);    // 更新服务器状态    d->state = QAbstractSocket::ListeningState;    // 获取实际绑定的地址和端口(如果port为0,这里会得到系统分配的端口)    d->address = d->socketEngine->localAddress();    d->port = d->socketEngine->localPort();#if defined (QTCPSERVER_DEBUG)    qDebug("QTcpServer::listen(%i, \"%s\") == true (listening on port %i)", port,           address.toString().toLatin1().constData(), d->socketEngine->localPort());#endif    return true;}/** * @brief 检查服务器是否正在监听连接 *  * @return 如果正在监听返回true,否则返回false *  * 通过检查套接字引擎的状态来判断服务器是否正在监听。 * 只有在成功调用listen()后才会返回true。 *  * @note 此方法会检查套接字引擎是否存在,如果不存在则返回false * @see listen(), close() */bool QTcpServer::isListening() const{    Q_D(const QTcpServer);    Q_CHECK_SOCKETENGINE(false);  // 如果套接字引擎不存在,返回false    return d->socketEngine->state() == QAbstractSocket::ListeningState;}/** * @brief 关闭服务器,停止监听连接 *  * 关闭服务器套接字,停止接受新的连接。已建立的连接不会被关闭。 *  * 清理过程: * 1. 删除所有待处理的连接(pendingConnections) * 2. 关闭套接字引擎 * 3. 延迟删除套接字引擎(使用deleteLater) * 4. 更新服务器状态为UnconnectedState *  * 内存管理: * - 使用qDeleteAll删除待处理连接列表中的所有套接字 * - 套接字引擎使用deleteLater延迟删除(避免在事件处理过程中删除) * - 如果内存不足,套接字引擎会在析构函数中删除(因为它是子对象) *  * @note 调用此方法后,isListening()将返回false * @note 已建立的客户端连接不会被关闭,需要单独处理 * @see listen(), isListening() */void QTcpServer::close(){    Q_D(QTcpServer);    // 删除所有待处理的连接    qDeleteAll(d->pendingConnections);    d->pendingConnections.clear();    // 关闭并删除套接字引擎    if(d->socketEngine) {        d->socketEngine->close();  // 关闭套接字        QT_TRY {            // 延迟删除套接字引擎(避免在事件处理过程中删除)            d->socketEngine->deleteLater();        } QT_CATCH(const std::bad_alloc &) {            // 内存不足时,套接字引擎会在析构函数中删除(因为它是子对象)        }        d->socketEngine = nullptr;    }    // 更新状态为未连接    d->state = QAbstractSocket::UnconnectedState;}/** * @brief 获取服务器套接字描述符 *  * @return 套接字描述符,如果未在监听则返回-1 *  * 返回底层服务器套接字的原生套接字描述符。 * 这个描述符可以用于: * - 平台特定的套接字操作 * - 传递给其他需要原生描述符的API * - 调试和监控 *  * 限制: * - 如果服务器使用QNetworkProxy,返回的描述符可能不能用于原生套接字函数 * - 描述符的类型取决于平台(Windows上是SOCKET,Unix上是int) *  * @note 在Windows上返回的是SOCKET类型,在Unix系统上返回的是int类型 * @see setSocketDescriptor(), isListening() */qintptr QTcpServer::socketDescriptor() const{    Q_D(const QTcpServer);    Q_CHECK_SOCKETENGINE(-1);  // 如果套接字引擎不存在,返回-1    return d->socketEngine->socketDescriptor();}/** * @brief 使用现有的套接字描述符初始化服务器 *  * 使用一个已经创建并绑定好的套接字描述符来初始化服务器。 * 这在需要与现有代码集成或使用特定套接字选项时很有用。 *  * @param socketDescriptor 已存在的套接字描述符 * @return 成功返回true,失败返回false *  * 前提条件: * - 套接字必须已经绑定到地址和端口 * - 套接字必须处于监听状态(已调用listen) * - 服务器当前不能正在监听 *  * 执行流程: * 1. 检查服务器是否已在监听,如果是则返回false * 2. 删除旧的套接字引擎(如果存在) * 3. 从描述符创建新的套接字引擎 * 4. 初始化套接字引擎(设置为监听状态) * 5. 设置事件接收器和读通知 * 6. 更新服务器状态和地址端口信息 *  * 使用场景: * - 与现有C代码集成 * - 需要设置特殊套接字选项 * - 从其他进程继承套接字 *  * @note 套接字描述符必须已经处于监听状态 * @see socketDescriptor(), isListening() */bool QTcpServer::setSocketDescriptor(qintptr socketDescriptor){    Q_D(QTcpServer);    // 检查是否已在监听    if(isListening()) {        qWarning("QTcpServer::setSocketDescriptor() called when already listening");        return false;    }    // 删除旧的套接字引擎(如果存在)    if(d->socketEngine)        delete d->socketEngine;    // 从描述符创建新的套接字引擎    d->socketEngine = QAbstractSocketEngine::createSocketEngine(socketDescriptor, this);    if(!d->socketEngine) {        // 创建失败:不支持的操作        d->serverSocketError = QAbstractSocket::UnsupportedSocketOperationError;        d->serverSocketErrorString = tr("Operation on socket is not supported");        return false;    }    // 初始化套接字引擎(设置为监听状态)    if(!d->socketEngine->initialize(socketDescriptor, QAbstractSocket::ListeningState)) {        // 初始化失败:记录错误信息        d->serverSocketError = d->socketEngine->error();        d->serverSocketErrorString = d->socketEngine->errorString();#if defined (QTCPSERVER_DEBUG)        qDebug("QTcpServer::setSocketDescriptor(%i) failed (%s)", socketDescriptor,               d->serverSocketErrorString.toLatin1().constData());#endif        return false;    }    // 设置事件接收器(用于接收readNotification回调)    d->socketEngine->setReceiver(d);    // 启用读通知(当有新连接到达时触发readNotification)    d->socketEngine->setReadNotificationEnabled(true);    // 更新服务器状态和地址端口信息    d->state = d->socketEngine->state();    d->address = d->socketEngine->localAddress();    d->port = d->socketEngine->localPort();#if defined (QTCPSERVER_DEBUG)    qDebug("QTcpServer::setSocketDescriptor(%i) succeeded.", socketDescriptor);#endif    return true;}/** * @brief 获取服务器正在监听的端口号 *  * @return 端口号,如果未在监听则返回0 *  * 返回服务器当前监听的端口号。 * 如果listen()时指定port为0(自动选择),此方法返回系统实际分配的端口号。 *  * @note 此方法会检查套接字引擎是否存在,如果不存在则返回0 * @see serverAddress(), listen() */quint16 QTcpServer::serverPort() const{    Q_D(const QTcpServer);    Q_CHECK_SOCKETENGINE(0);  // 如果套接字引擎不存在,返回0    return d->socketEngine->localPort();}/** * @brief 获取服务器正在监听的IP地址 *  * @return 服务器地址,如果未在监听则返回QHostAddress::Null *  * 返回服务器当前监听的IP地址。 * 如果listen()时指定address为QHostAddress::Any,此方法返回QHostAddress::Any。 *  * @note 此方法会检查套接字引擎是否存在,如果不存在则返回QHostAddress::Null * @see serverPort(), listen() */QHostAddress QTcpServer::serverAddress() const{    Q_D(const QTcpServer);    Q_CHECK_SOCKETENGINE(QHostAddress(QHostAddress::Null));  // 如果套接字引擎不存在,返回Null地址    return d->socketEngine->localAddress();}/** * @brief 等待新连接(阻塞方式) *  * 阻塞等待直到有新连接可用或超时。这个方法主要用于不使用事件循环的场景。 *  * @param msec 超时时间(毫秒),0表示不超时,-1表示使用默认超时 * @param timedOut 可选参数,如果连接超时则设置为true * @return 如果有新连接返回true,超时或出错返回false *  * 执行流程: * 1. 检查服务器是否在监听状态 * 2. 调用套接字引擎的waitForRead()等待读事件 * 3. 如果有超时或错误,记录错误信息并返回false * 4. 如果有读事件,调用readNotification()处理新连接 * 5. 返回true表示有新连接可用 *  * 使用场景: * - 单线程服务器应用(没有事件循环) * - 需要同步处理连接的场景 * - 命令行工具或后台服务 *  * 注意事项: * - 这是阻塞调用,会暂停当前线程的执行 * - 在GUI应用中不推荐使用,会导致界面冻结 * - 在有事件循环的情况下,应该使用newConnection()信号 *  * @note 如果msec为-1,此函数不会超时 * @see newConnection(), hasPendingConnections(), nextPendingConnection() */bool QTcpServer::waitForNewConnection(int msec, bool *timedOut){    Q_D(QTcpServer);    // 检查服务器是否在监听状态    if(d->state != QAbstractSocket::ListeningState)        return false;    // 等待读事件(阻塞调用)    if(!d->socketEngine->waitForRead(msec, timedOut)) {        // 等待失败:记录错误信息        d->serverSocketError = d->socketEngine->error();        d->serverSocketErrorString = d->socketEngine->errorString();        return false;    }    // 检查是否超时    if(timedOut && *timedOut)        return false;    // 处理新连接(调用readNotification处理所有待接受的连接)    d->readNotification();    return true;}/** * @brief 检查是否有待处理的连接 *  * @return 如果有待处理连接返回true,否则返回false *  * 检查是否有已建立但尚未通过nextPendingConnection()接受的连接。 *  * @note 这是一个虚函数,子类可以重写以自定义行为 * @note 使用d_func()宏访问私有数据,避免暴露私有实现 * @see nextPendingConnection(), setMaxPendingConnections(), newConnection() */bool QTcpServer::hasPendingConnections() const{    return !d_func()->pendingConnections.isEmpty();}/** * @brief 获取下一个待处理的连接 *  * @return 指向QTcpSocket对象的指针,如果没有待处理连接则返回nullptr *  * 从待处理连接队列中取出下一个连接,返回一个已连接的QTcpSocket对象。 *  * 所有权: * - 返回的套接字对象的所有权转移给调用者 * - 套接字是服务器的子对象,会在服务器销毁时自动删除 * - 建议在使用完后显式删除,避免浪费内存 *  * 线程安全: * - 返回的QTcpSocket对象不能从其他线程使用 * - 如果要在其他线程中使用连接,需要重写incomingConnection() *  * 自动恢复监听: * - 如果之前因为达到最大连接数而暂停了接受,此方法会自动恢复 *  * @note 这是一个虚函数,子类可以重写以返回自定义的套接字类型(如QSslSocket) * @note 返回的套接字对象必须手动删除(或设置新的父对象) * @see hasPendingConnections(), newConnection(), addPendingConnection() */QTcpSocket *QTcpServer::nextPendingConnection(){    Q_D(QTcpServer);    // 检查是否有待处理连接    if(d->pendingConnections.isEmpty())        return nullptr;    // 检查套接字引擎状态    if(!d->socketEngine) {        qWarning("QTcpServer::nextPendingConnection() called while not listening");    } else if (!d->socketEngine->isReadNotificationEnabled()) {        // 如果之前因为达到最大连接数而暂停了接受,现在恢复        d->socketEngine->setReadNotificationEnabled(true);    }    // 从队列中取出第一个连接并返回    return d->pendingConnections.takeFirst();}/** * @brief 处理新连接(虚函数,可重写) *  * 当有新连接到达时,框架会调用此方法。默认实现创建一个QTcpSocket对象 * 并将其添加到待处理连接队列。 *  * @param socketDescriptor 新连接的套接字描述符 *  * 默认实现: * 1. 创建一个QTcpSocket对象(作为服务器的子对象) * 2. 设置套接字描述符 * 3. 调用addPendingConnection()将套接字添加到待处理队列 *  * 重写场景: * - 创建自定义的套接字类型(如QSslServer创建QSslSocket) * - 执行连接前的验证或初始化 * - 实现自定义的连接处理逻辑 * - 将连接传递给其他线程处理 *  * 重要提示: * - 如果使用QNetworkProxy,socketDescriptor可能不能用于原生套接字函数 * - 如果创建了其他套接字,必须调用addPendingConnection()添加到队列 * - 如果要在其他线程处理连接,需要传递socketDescriptor到该线程 *  * @note 子类重写时通常应该调用addPendingConnection()将套接字添加到队列 * @see addPendingConnection(), nextPendingConnection(), newConnection() */void QTcpServer::incomingConnection(qintptr socketDescriptor){#if defined (QTCPSERVER_DEBUG)    qDebug("QTcpServer::incomingConnection(%i)", socketDescriptor);#endif    // 创建新的TCP套接字对象(作为服务器的子对象,自动管理生命周期)    QTcpSocket *socket = new QTcpSocket(this);    // 设置套接字描述符(将原生描述符关联到QTcpSocket)    socket->setSocketDescriptor(socketDescriptor);    // 添加到待处理连接队列(会触发pendingConnectionAvailable信号)    addPendingConnection(socket);}/** * @brief 添加套接字到待处理连接队列 *  * 将已连接的套接字添加到待处理连接队列。通常在重写incomingConnection()时调用。 *  * @param socket 要添加的QTcpSocket对象指针 *  * 功能: * - 将套接字添加到pendingConnections列表 * - 发出pendingConnectionAvailable()信号通知应用程序 *  * 重要提示: * - 套接字必须已经处于连接状态 * - 如果重写了incomingConnection(),必须调用此方法以保持待处理连接机制正常工作 * - 此方法会发出pendingConnectionAvailable()信号 *  * @note 使用d_func()宏访问私有数据 * @note 此方法自Qt 4.7版本引入 * @see incomingConnection(), pendingConnectionAvailable(), nextPendingConnection() */void QTcpServer::addPendingConnection(QTcpSocket* socket){    // 将套接字添加到待处理连接列表    d_func()->pendingConnections.append(socket);    // 发出待处理连接可用信号(使用QPrivateSignal防止外部连接)    emit pendingConnectionAvailable(QPrivateSignal());}/** * @brief 设置最大待处理连接数 *  * 设置服务器在等待调用nextPendingConnection()接受连接之前, * 可以保持的最大待处理连接数。 *  * @param numConnections 最大待处理连接数 *  * 工作原理: * - 当待处理连接数达到此限制时,服务器会暂停接受新连接 * - 调用nextPendingConnection()取出连接后,会自动恢复接受 * - 默认值为30个连接 *  * 重要说明: * - 客户端可能仍然能够连接(操作系统可能保持连接在队列中) * - QTcpSocket仍可能发出connected()信号 * - QTcpServer会停止接受新连接,但操作系统可能继续排队 *  * @note 此设置影响的是待处理连接队列的大小,不是总连接数 * @see maxPendingConnections(), hasPendingConnections() */void QTcpServer::setMaxPendingConnections(int numConnections){    d_func()->maxConnections = numConnections;}/** * @brief 获取最大待处理连接数 *  * @return 当前设置的最大待处理连接数(默认30) *  * @see setMaxPendingConnections() */int QTcpServer::maxPendingConnections() const{    return d_func()->maxConnections;}/** * @brief 设置监听积压队列大小 *  * 设置底层套接字监听积压队列的大小。这个队列存储了已完成三次握手 * 但尚未被accept()接受的连接。 *  * @param size 积压队列大小 *  * 说明: * - 较大的值可以处理更多的并发连接请求,但会消耗更多系统资源 * - 操作系统可能会减少或忽略此值(受系统限制) * - 默认队列大小为50 *  * 重要提示: * - 此属性必须在调用listen()之前设置 * - 此方法自Qt 6.3版本引入 *  * @see listenBacklogSize(), listen() */void QTcpServer::setListenBacklogSize(int size){    d_func()->listenBacklog = size;}/** * @brief 获取监听积压队列大小 *  * @return 当前设置的积压队列大小(默认50) *  * @note 此方法自Qt 6.3版本引入 * @see setListenBacklogSize() */int QTcpServer::listenBacklogSize() const{    return d_func()->listenBacklog;}/** * @brief 获取最后一次发生的服务器错误 *  * @return 错误类型枚举值 *  * 返回服务器最后一次发生的错误类型。 * 如果没有错误,返回QAbstractSocket::UnknownSocketError。 *  * @see errorString(), QAbstractSocket::SocketError */QAbstractSocket::SocketError QTcpServer::serverError() const{    return d_func()->serverSocketError;}/** * @brief 获取最后一次错误的可读描述 *  * @return 错误描述字符串 *  * 返回一个人类可读的错误描述字符串,说明最后一次发生的错误。 * 通常用于日志记录和用户提示。 *  * @see serverError() */QString QTcpServer::errorString() const{    return d_func()->serverSocketErrorString;}/** * @brief 暂停接受新连接 *  * 临时暂停接受新的连接请求。已建立的连接不受影响。 *  * 功能: * - 禁用套接字引擎的读通知 * - 停止接受新的连接请求 * - 已排队的连接保持在队列中 *  * 使用场景: * - 服务器负载过高时临时停止接受新连接 * - 进行维护操作时暂停服务 * - 需要处理完现有连接后再接受新连接 *  * @note 此方法自Qt 5.0版本引入 * @see resumeAccepting() */void QTcpServer::pauseAccepting(){    // 禁用读通知,停止接受新连接    d_func()->socketEngine->setReadNotificationEnabled(false);}/** * @brief 恢复接受新连接 *  * 恢复接受新的连接请求。只有在之前调用了pauseAccepting()时才需要调用此方法。 *  * 功能: * - 启用套接字引擎的读通知 * - 恢复接受新的连接请求 *  * @note 此方法自Qt 5.0版本引入 * @see pauseAccepting() */void QTcpServer::resumeAccepting(){    // 启用读通知,恢复接受新连接    d_func()->socketEngine->setReadNotificationEnabled(true);}#ifndef QT_NO_NETWORKPROXY/** * @brief 设置网络代理 *  * 为服务器设置网络代理。代理设置会影响服务器如何建立连接。 *  * @param networkProxy 网络代理配置对象 *  * 禁用代理: * 要禁用此服务器的代理使用,使用QNetworkProxy::NoProxy类型: * @code * server->setProxy(QNetworkProxy::NoProxy); * @endcode *  * 重要提示: * - 必须在调用listen()之前设置代理 * - 代理设置会影响后续的所有连接操作 *  * @note 此方法自Qt 4.1版本引入 * @see proxy(), QNetworkProxy */void QTcpServer::setProxy(const QNetworkProxy &networkProxy){    Q_D(QTcpServer);    d->proxy = networkProxy;}/** * @brief 获取当前网络代理配置 *  * @return 当前设置的网络代理对象 *  * 默认情况下使用QNetworkProxy::DefaultProxy,会查询应用程序的代理设置。 *  * @note 此方法自Qt 4.1版本引入 * @see setProxy(), QNetworkProxy */QNetworkProxy QTcpServer::proxy() const{    Q_D(const QTcpServer);    return d->proxy;}#endif // QT_NO_NETWORKPROXYQT_END_NAMESPACE// 包含MOC生成的元对象代码(用于信号槽机制)#include "moc_qtcpserver.cpp"

希望本文能为您的C++/Qt学习之旅提供有价值的参考!欢迎关注【Qt开发宝典】微信公众号,获取更多技术干货。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-07 15:20:26 HTTP/2.0 GET : https://f.mffb.com.cn/a/471800.html
  2. 运行时间 : 0.256231s [ 吞吐率:3.90req/s ] 内存消耗:5,147.27kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=d822344583bd378246b506bf826eb7e5
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000435s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000548s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.013241s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.005263s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001487s ]
  6. SELECT * FROM `set` [ RunTime:0.000775s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001516s ]
  8. SELECT * FROM `article` WHERE `id` = 471800 LIMIT 1 [ RunTime:0.002968s ]
  9. UPDATE `article` SET `lasttime` = 1770448827 WHERE `id` = 471800 [ RunTime:0.025814s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.002603s ]
  11. SELECT * FROM `article` WHERE `id` < 471800 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.003414s ]
  12. SELECT * FROM `article` WHERE `id` > 471800 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.013640s ]
  13. SELECT * FROM `article` WHERE `id` < 471800 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.013874s ]
  14. SELECT * FROM `article` WHERE `id` < 471800 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.008348s ]
  15. SELECT * FROM `article` WHERE `id` < 471800 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.003384s ]
0.260092s