当前位置: 首页 > news >正文

粗解JQHttpServer

JQHttpServer是基于Qt开发的轻量级HTTP/HTTPS服务器。

GitHub地址:GitHub - 188080501/JQHttpServer: 基于Qt开发的轻量级HTTP/HTTPS服务器
不愧是大神写的,用起来确实方便。

下面粗略介绍 jqhttpserver.h 的内容先看源代码:
 

/*
    This file is part of JQLibrary

    Copyright: Jason

    Contact email: 188080501@qq.com

    GNU Lesser General Public License Usage
    Alternatively, this file may be used under the terms of the GNU Lesser
    General Public License version 2.1 or version 3 as published by the Free
    Software Foundation and appearing in the file LICENSE.LGPLv21 and
    LICENSE.LGPLv3 included in the packaging of this file. Please review the
    following information to ensure the GNU Lesser General Public License
    requirements will be met: https://www.gnu.org/licenses/lgpl.html and
    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
*/

#ifndef JQLIBRARY_INCLUDE_JQHTTPSERVER_H_
#define JQLIBRARY_INCLUDE_JQHTTPSERVER_H_

#ifndef QT_NETWORK_LIB
#   error("Please add network in pro file")
#endif

#ifndef QT_CONCURRENT_LIB
#   error("Please add concurrent in pro file")
#endif

// C++ lib import
#include <functional>

// Qt lib import
#include <QVector>
#include <QFuture>
#include <QMutex>
#include <QUrl>
#ifndef QT_NO_SSL
#   include <QSslCertificate>
#   include <QSslSocket>
#endif

#include "jqdeclare.hpp"

class QIODevice;
class QThreadPool;
class QHostAddress;
class QTimer;
class QImage;
class QTcpServer;
class QLocalServer;
class QSslKey;
class QSslConfiguration;

namespace JQHttpServer
{

class Session: public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY( Session )

public:
    Session( const QPointer<QIODevice> &socket );

    ~Session();

    void setHandleAcceptedCallback(const std::function<void(const QPointer<Session> &)> &callback) { handleAcceptedCallback_ = callback; }

    QPointer<QIODevice> ioDevice() { return ioDevice_; }

    QString requestSourceIp() const;

    QString requestMethod() const;

    QString requestUrl() const;

    QString requestCrlf() const;

    QMap<QString, QString> requestHeader() const;

    QByteArray requestBody() const;

    QString requestUrlPath() const;

    QStringList requestUrlPathSplitToList() const;

    QMap<QString, QString> requestUrlQuery() const;

    int replyHttpCode() const;

    qint64 replyBodySize() const;

#ifndef QT_NO_SSL
    QSslCertificate peerCertificate() const;
#endif

    volatile int m_isSafeExit;

public slots:
    void replyText(const QString &replyData, const int &httpStatusCode = 200);

    void replyRedirects(const QUrl &targetUrl, const int &httpStatusCode = 200);

    void replyJsonObject(const QJsonObject &jsonObject, const int &httpStatusCode = 200);

    void replyJsonArray(const QJsonArray &jsonArray, const int &httpStatusCode = 200);

    void replyFile(const QString &filePath, const int &httpStatusCode = 200);

    void replyFile(const QString &fileName, const QByteArray &fileData, const int &httpStatusCode = 200);

    void replyImage(const QImage &image, const QString &format = "PNG", const int &httpStatusCode = 200);

    void replyImage(const QString &imageFilePath, const int &httpStatusCode = 200);

    void replyBytes(const QByteArray &bytes, const QString &contentType = "application/octet-stream", const int &httpStatusCode = 200);

    void replyOptions();

private:
    void inspectionBufferSetup1();
    void inspectionBufferSetup2();

    void onBytesWritten(const qint64 &written);
    void waitWorkingForFinished();

private:
    static QAtomicInt remainSession_;

    QPointer<QIODevice>                                   ioDevice_;
    std::function<void( const QPointer<Session> & )>      handleAcceptedCallback_;
    QSharedPointer<QTimer>                                autoCloseTimer_;

    QByteArray receiveBuffer_;

    QString                  requestSourceIp_;
    QString                  requestMethod_;
    QString                  requestUrl_;
    QString                  requestCrlf_;
    QByteArray               requestBody_;
    QMap<QString, QString> requestHeader_;

    bool   headerAcceptedFinished_  = false;
    bool   contentAcceptedFinished_ = false;
    qint64 contentLength_           = -1;

    int        replyHttpCode_ = -1;
    QByteArray replyBuffer_;
    qint64     replyBodySize_ = -1;

    qint64                      waitWrittenByteCount_ = -1;
    QSharedPointer<QIODevice> replyIoDevice_;
};

class AbstractManage: public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY( AbstractManage )

public:
    AbstractManage(const int &handleMaxThreadCount);

    virtual ~AbstractManage();

    void setMainObject(QObject *obj) { m_mainObj = obj; }
    QObject* getMainObject() { return m_mainObj; }

    void setHttpAcceptedCallback(const std::function<void(const QPointer<Session> &session,QObject *mainObj)> &httpAcceptedCallback)
    {
        httpAcceptedCallback_ = httpAcceptedCallback;
    }

    QSharedPointer<QThreadPool> handleThreadPool() { return handleThreadPool_; }

    QSharedPointer<QThreadPool> serverThreadPool() { return serverThreadPool_; }

    virtual bool isRunning() = 0;

protected Q_SLOTS:
    bool initialize();

    void deinitialize();

protected:
    virtual bool onStart() = 0;

    virtual void onFinish() = 0;

    bool startServerThread();

    void stopHandleThread();

    void stopServerThread();

    void newSession(const QPointer<Session> &session);

    void handleAccepted(const QPointer<Session> &session);

signals:
    void readyToClose();
    //void onRedReady(const QPointer<JQHttpServer::Session> &session);

protected:
    QSharedPointer<QThreadPool> serverThreadPool_;
    QSharedPointer<QThreadPool> handleThreadPool_;

    QMutex mutex_;

    std::function<void(const QPointer<Session> &session,QObject *mainObj)> httpAcceptedCallback_;

    QSet<Session *> availableSessions_;

    QObject *m_mainObj;
};

class TcpServerManage: public AbstractManage
{
    Q_OBJECT
    Q_DISABLE_COPY( TcpServerManage )

public:
    TcpServerManage(const int &handleMaxThreadCount = 2);

    ~TcpServerManage();

    bool listen( const QHostAddress &address, const quint16 &port );
    quint16 getListenPort() { return listenPort_; }

private:
    bool isRunning();

    bool onStart();

    void onFinish();

private:
    QPointer<QTcpServer> tcpServer_;

    QHostAddress listenAddress_ = QHostAddress::Any;
    quint16 listenPort_ = 0;
};

#ifndef QT_NO_SSL
class SslServerHelper;

class SslServerManage: public AbstractManage
{
    Q_OBJECT
    Q_DISABLE_COPY( SslServerManage )

public:
    SslServerManage(const int &handleMaxThreadCount = 2);

    ~SslServerManage();

    bool listen( const QHostAddress &                                   address,
                 const quint16 &                                        port,
                 const QString &                                        crtFilePath,
                 const QString &                                        keyFilePath,
                 const QList<QPair<QString, QSsl::EncodingFormat>> &caFileList = {},    // [ { filePath, format } ]
                 const QSslSocket::PeerVerifyMode &                     peerVerifyMode = QSslSocket::VerifyNone );
    quint16 getListenPort() { return listenPort_; }

private:
    bool isRunning();

    bool onStart();

    void onFinish();

private:
    QPointer<SslServerHelper> tcpServer_;

    QHostAddress listenAddress_ = QHostAddress::Any;
    quint16      listenPort_    = 0;

    QSharedPointer<QSslConfiguration> sslConfiguration_;
};


enum ServiceConfigEnum
{
    ServiceUnknownConfig,
    ServiceHttpListenPort,
    ServiceHttpsListenPort,
    ServiceProcessor, // QPointer<QObject> or QList<QPointer<QObject>>
    ServiceUuid,
    ServiceSslCrtFilePath,
    ServiceSslKeyFilePath,
    ServiceSslCAFilePath,
    ServiceSslPeerVerifyMode,
};

class Service: public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY( Service )

private:
    enum ReceiveDataType
    {
        UnknownReceiveDataType,
        NoReceiveDataType,
        VariantListReceiveDataType,
        VariantMapReceiveDataType,
        ListVariantMapReceiveDataType,
    };

    struct ApiConfig
    {
        QPointer<QObject> process;
        QString             apiMethod;
        QString             apiName;
        QString             slotName;
        ReceiveDataType     receiveDataType = UnknownReceiveDataType;
    };

    class Recoder
    {
    public:
        Recoder( const QPointer<JQHttpServer::Session> &session );

        ~Recoder();

        QPointer<JQHttpServer::Session> session_;
        QDateTime                         acceptedTime_;
        QString                           serviceUuid_;
        QString                           apiName;
    };

protected:
    Service() = default;

public:
    ~Service() = default;


    static QSharedPointer<Service> createService( const QMap<ServiceConfigEnum, QVariant> &config );


    void registerProcessor( const QPointer<QObject> &processor );


    virtual QJsonDocument extractPostJsonData( const QPointer<JQHttpServer::Session> &session );

    static void reply(
        const QPointer<JQHttpServer::Session> &session,
        const QJsonObject &data,
        const bool &isSucceed = true,
        const QString &message = { },
        const int &httpStatusCode = 200 );

    static void reply(
        const QPointer<JQHttpServer::Session> &session,
        const bool &isSucceed = true,
        const QString &message = { },
        const int &httpStatusCode = 200 );


    virtual void httpGetPing( const QPointer<JQHttpServer::Session> &session );

    virtual void httpGetFaviconIco( const QPointer<JQHttpServer::Session> &session );

    virtual void httpOptions( const QPointer<JQHttpServer::Session> &session );

protected:
    bool initialize( const QMap<ServiceConfigEnum, QVariant> &config );

private:
    void onSessionAccepted( const QPointer<JQHttpServer::Session> &session );


    static QString snakeCaseToCamelCase(const QString &source, const bool &firstCharUpper = false);

    static QList<QVariantMap> variantListToListVariantMap(const QVariantList &source);

private:
    QSharedPointer<JQHttpServer::TcpServerManage> httpServerManage_;
    QSharedPointer<JQHttpServer::SslServerManage> httpsServerManage_;

    QString                                     serviceUuid_;
    QMap<QString, QMap<QString, ApiConfig>> schedules_;    // apiMethod -> apiName -> API
    QMap<QString, std::function<void( const QPointer<JQHttpServer::Session> &session )>> schedules2_; // apiPathPrefix -> callback
    QPointer<QObject> certificateVerifier_;
};
#endif

}

#endif//JQLIBRARY_INCLUDE_JQHTTPSERVER_H_

1. Session 类

作用:处理单个 HTTP 请求/响应会话的全生命周期

核心功能

  • 请求解析
    存储 HTTP 请求的元数据(如 requestMethod_requestUrl_requestHeader_ 等),提供接口获取请求的:

    • 客户端 IP (requestSourceIp())

    • 请求方法(GET/POST等)

    • URL 路径和查询参数

    • 请求头和请求体内容

  • 响应生成
    提供多种响应方式:

    • 文本/JSON/二进制数据回复(replyText()/replyJsonObject()/replyBytes()

    • 文件/图片传输(replyFile()/replyImage()

    • 重定向(replyRedirects()

    • 支持设置 HTTP 状态码和自定义 Content-Type

  • 底层通信
    通过 QIODevice(如 QTcpSocket)与客户端进行数据交互,管理数据的异步写入(onBytesWritten() 处理写入进度)

  • 安全控制
    支持 SSL 证书验证(peerCertificate()),提供自动关闭连接的定时器(autoCloseTimer_

典型使用场景
当服务器接受一个新连接时,会创建一个 Session 对象,该对象解析客户端请求并调用相应的回复方法发送响应数据。


2. AbstractManage 类

作用:服务器管理的抽象基类,提供线程池和会话管理的基础设施

核心功能

  • 线程池管理
    维护两个线程池:

    • handleThreadPool_:处理业务逻辑(如调用用户设置的 httpAcceptedCallback_

    • serverThreadPool_:处理网络监听和连接接受(通过 startServerThread()

  • 会话生命周期管理
    跟踪所有活跃的 Session 对象(availableSessions_),提供统一的初始化/反初始化接口(initialize()/deinitialize()

  • 回调机制
    通过 setHttpAcceptedCallback() 允许用户注册自定义请求处理逻辑,当新会话建立时,触发 handleAccepted() 分发到线程池

  • 抽象接口
    定义纯虚函数 onStart() 和 onFinish(),要求子类实现具体的服务器启动/停止逻辑

设计目的
为不同类型的服务器(如 TCP、SSL、Local Socket)提供统一的管理框架,复用线程管理和会话跟踪逻辑。


3. TcpServerManage 类

作用:基于 TCP 协议的 HTTP 服务器具体实现

核心功能

  • TCP 服务器监听
    通过 listen() 方法在指定地址和端口启动 QTcpServer,等待客户端连接

  • 连接处理
    当新连接到达时,创建 Session 对象并调用 newSession(),将其加入管理队列

  • 继承实现
    实现父类 AbstractManage 的纯虚函数:

    • onStart():启动 TCP 服务器

    • onFinish():关闭服务器并清理资源

关键成员

  • QTcpServer* tcpServer_:底层 TCP 服务器实例

  • listenAddress_ 和 listenPort_:监听的地址和端口

使用场景
需快速搭建一个基于 TCP 的 HTTP 服务器时,直接实例化此类并调用 listen() 方法即可。


协作流程示例

  1. 启动服务器
    TcpServerManage 调用 listen() → 触发 onStart() 启动 QTcpServer

  2. 接受连接
    当客户端连接到达 → TcpServerManage 创建 Session 对象,并调用 AbstractManage::newSession()

  3. 处理请求
    AbstractManage 将新会话通过 httpAcceptedCallback_ 回调传递给用户代码,使用 handleThreadPool_ 异步处理

  4. 发送响应
    用户在回调中通过 Session::replyXXX() 方法生成响应数据 → 数据通过 QIODevice 异步发送给客户端

  5. 资源回收
    会话结束时,Session 的 autoCloseTimer_ 或写入完成信号触发资源释放,AbstractManage 维护的 availableSessions_ 自动清理失效会话。

http://www.dtcms.com/a/73572.html

相关文章:

  • 如何优化 TCP/IP 的 NCCL 通信
  • 地图(六)利用python绘制连接地图
  • 【QT:窗口】
  • 小程序API —— 51小程序界面交互 - loading 提示框
  • 旅游类小程序界面设计
  • 基于HetEmotionNet框架的多模态情绪识别系统
  • 实战2. 利用Pytorch解决 CIFAR 数据集中的图像分类为 10 类的问题——提高精度
  • 施磊老师c++(八)
  • 唤起“栈”的回忆
  • 【数据结构】栈与队列:基础 + 竞赛高频算法实操(含代码实现)
  • Web测试
  • 神聖的綫性代數速成例題7. 逆矩陣的性質、逆矩陣的求法
  • 深度学习-yolo实战项目【分类、目标检测、实例分割】?如何创建自己的数据集?如何对数据进行标注?没有GPU怎么办呢?
  • 计算机网络基础:网络配置与管理
  • ImGui 学习笔记(五) —— 字体文件加载问题
  • Redis集群扩容实战指南:从原理到生产环境最佳实践
  • DICOM医学影像数据加密技术应用的重要性及其实现方法详解
  • 优选算法的匠心之艺:二分查找专题(二)
  • 双模型协作机制的deepseek图片识别
  • Linux错误(2)程序触发SIGBUS信号分析
  • CTF类题目复现总结-真的很杂 1
  • Spring Boot 集成 Lua 脚本:实现高效业务逻辑处理
  • 【小项目】四连杆机构的Python运动学求解和MATLAB图形仿真
  • Elasticsearch:为推理端点配置分块设置
  • 【微服务】SpringBoot整合LangChain4j 操作AI大模型实战详解
  • Qt SQL-1
  • 基于MapReduce的气候数据分析
  • [JAVASE] 反射
  • USB转多路串口项目资料汇总
  • 第九讲 排序(上)