QT httpServer多线程后台服务器的例子实现

1.需求

1.1 用户需要其他平台(web端)调用Qt平台的接口,获取想要的数据并实时显示在网页里,比如实时的温湿度,用户数据等

1.2 用户需要在其他平台(web端)调用Qt平台的接口,下发数据给本地QT客户端显示,如下发用户数据,下发任务等

2.解决方案

这是就需要一个类似httpServer的服务端了,实时监听端口,随时接收web平台的请求,根据请求内容,接收平台下发的数据并存储到本地客户端显示,或者根据请求,上传需要的信息给web平台

现成的接口是没有的,需要自己写,底层本质都是基于QWebServer加上多线程封装实现的,轮子是已经有的,已经造好了,我们用就行了,想深入了解的,可以看源码的实现

我就挂在下面的链接里了,开源的Qt httpServer代码,里面有很多

链接: https://pan.baidu.com/s/1SHqSCGiGQblur69oCz_LXg?pwd=1234 提取码: 1234

3. 实现

建立一个WebServerApi的工程项目,没有界面的,一般后台服务都是没有界面的,更加轻便,反应快,可以建立控制台项目或者动态库/插件库,都可以,我这里用来演示,就用控制台项目演示了

建好工程项目后,在有了已经写好的轮子基础上,就简单了,先把需要的httpServer文件引入程序目录里

然后建一个WebApi1的类,用来表示一个接口类,专门处理Api1接口的内容

cpp 复制代码
#ifndef WEBAPI1_H
#define WEBAPI1_H

#include "httprequesthandler.h"
using namespace stefanfrings;

class WebApi1: public HttpRequestHandler
{
public:
    WebApi1(QObject *parent = nullptr);
    void service(HttpRequest &request, HttpResponse &response) override;
    QJsonObject changeByteArrayToJsonObject(const QByteArray &ba);
};

#endif // WEBAPI1_H
cpp 复制代码
#include "webapi1.h"
#include <QJsonObject>
#include <QJsonDocument>


WebApi1::WebApi1(QObject *parent)
{

}

void WebApi1::service(HttpRequest &request, HttpResponse &response)
{
    QString path = request.getPath();
    QStringList pathList = path.split("/");
    //QString interfaceName = pathList.value(3);
    if (pathList.size()<3) {
        this->returnError(response);
        return;
    }
    QString method = pathList.value(3);
    QByteArray responseMsg;

    if(method == "getUserInfo") {
        QJsonObject obj;
        obj.insert("name", "小明");
        obj.insert("age", 18);
        obj.insert("success", true);
        responseMsg =  QJsonDocument(obj).toJson();
    }
    else if(method == "setUserInfo") {
        auto recvData = request.getBody();
        QJsonObject data = changeByteArrayToJsonObject(recvData);

        QString name = data.value("name").toString();
        int age = data.value("name").toInt();
        qDebug()<<QString("收到平台下发用户信息,name:%1,age:%2").arg(name).arg(age);

        QJsonObject obj;
        obj.insert("success", true);
        responseMsg =  QJsonDocument(obj).toJson();
    }
    else {
        QJsonObject obj;
        obj.insert("error", "没有这个方法");
        obj.insert("success", false);
        responseMsg =  QJsonDocument(obj).toJson();
    }

    response.write(responseMsg);
}

QJsonObject WebApi1::changeByteArrayToJsonObject(const QByteArray &ba)
{
    // 将数据转化成json内容
    QJsonParseError jsonpe;
    QJsonDocument json = QJsonDocument::fromJson(ba, &jsonpe);
    if (jsonpe.error == QJsonParseError::NoError) {
        if (json.isObject()) {
            QJsonObject obj = json.object();
            if (obj.contains("error")) {
                qDebug() << "error:" << obj["error"];
                return QJsonObject();
            } else {
                return obj;
            }
        } else {
            qDebug() << "error, shoud json object";
            return QJsonObject();
        }
    } else {
        qDebug() << "error:" << jsonpe.errorString();
        return QJsonObject();
    }
}

然后在建一个WebApi2的接口类,同上,名字不一样而已,我用来演示

在建一个RequestMapper类,用来做请求映射的管理,也就是对于每一个不同类型接口,可以根据定义好的类型来做出相应的处理,内容如下:

cpp 复制代码
#ifndef REQUESTMAPPER_H
#define REQUESTMAPPER_H

#include "httprequesthandler.h"

using namespace stefanfrings;

class RequestMapper : public HttpRequestHandler
{
public:
    RequestMapper(QObject *parent = nullptr);

    ~RequestMapper() override;

    void service(HttpRequest &request, HttpResponse &response) override;

private:
    QHash<QString, HttpRequestHandler *> m_requestMap;

};

#endif // REQUESTMAPPER_H
cpp 复制代码
#include "requestmapper.h"
#include <QDateTime>
#include "webapi1.h"
#include "webapi2.h"

RequestMapper::RequestMapper(QObject *parent)
{
    m_requestMap.insert("WebApi1", new WebApi1(this));
    m_requestMap.insert("WebApi2", new WebApi2(this));
}

RequestMapper::~RequestMapper() {
    qDeleteAll(m_requestMap);
    m_requestMap.clear();
}

void RequestMapper::service(HttpRequest &request, HttpResponse &response) {
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Credentials", "true");
    response.setHeader("P3P", "CP=CAO PSA OUR");
    if (!request.getHeader("Access-Control-Request-Method").isNull() && request.getMethod() == "OPTIONS") {
        response.setHeader("Access-Control-Allow-Methods", "POST,GET,TRACE,OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type,Origin,Accept");
        response.setHeader("Access-Control-Max-Age", 86400);
    }
    response.setHeader("Content-Type", "application/json;charset=utf-8");
    response.setHeader("Date", QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss").toUtf8());
    QString path = request.getPath();
    QStringList pathList = path.split("/");
    // 因为path以/为开头,使用split,第一个元素为空串
    if (pathList.size() < 3+1) {
        this->returnError(response);
        return;
    }
    if (pathList.value(1) != "Stms") {
        this->returnError(response);
        return;
    }
    // 如请url后缀为 /test/Stms/WebApi1

    HttpRequestHandler *handler = m_requestMap.value(pathList.value(2));
    if (handler != nullptr) {
        handler->service(request, response);
    } else {
        this->returnError(response);
    }
}
复制代码
在建立一个WebApiManager的管理类,内容如下
cpp 复制代码
#ifndef WEBAPIMANAGER_H
#define WEBAPIMANAGER_H

#include <QObject>

class WebApiManager: public QObject
{
public:
    WebApiManager(QObject *parent = nullptr);

    void init();
};


#endif // WEBAPIMANAGER_H
cpp 复制代码
#include "webapimanager.h"
#include "requestmapper.h"
#include "httplistener.h"
#include "httpsessionstore.h"
#include "staticfilecontroller.h"
#include <QCoreApplication>
#include <QSettings>

StaticFileController* s_staticFileController = nullptr;

using namespace stefanfrings;

WebApiManager::WebApiManager(QObject *parent): QObject(parent)
{

}

void WebApiManager::init()
{
    QString configFileName = QCoreApplication::applicationDirPath() + "/config/WebConfig.ini";

    // Configure logging into a file
//    QSettings *logSettings = new QSettings(configFileName, QSettings::IniFormat, this);
//    logSettings->beginGroup("logging");
//    auto logger = new FileLogger(logSettings, 10000, this);
//#ifndef QT_DEBUG
//    logger->installMsgHandler();
//#endif

    // Configure session store
    QSettings *sessionSettings = new QSettings(configFileName, QSettings::IniFormat, this);
    sessionSettings->beginGroup("sessions");
    new HttpSessionStore(sessionSettings, this);

    // Configure static file controller
    //    QSettings *fileSettings = new QSettings(configFileName, QSettings::IniFormat, this);
    //    fileSettings->beginGroup("docroot");
    //    s_staticFileController = new StaticFileController(fileSettings, this);

    // Configure and start the TCP listener
    QSettings *listenerSettings = new QSettings(configFileName, QSettings::IniFormat, this);
    listenerSettings->beginGroup("listener");
    new HttpListener(listenerSettings, new RequestMapper(this), this);
}

在init()的函数中,初始化了webServer的配置信息,这里用的是配置文件(WebConfig.ini)的方式进行配置,如监听的端口,还有一些其他的配置

配置文件内容如下,可配置监听的端口,线程数等,还有其他的信息不就不解析了,想知道的可以去查

cpp 复制代码
[listener]
host=0.0.0.0
#监听端口
port=8080
#最小线程数量
minThreads=4
#最大线程数量
maxThreads=100
#自动清理延时
cleanupInterval=60000
#读取超时时长
readTimeout=60000
#证书秘钥文件
#sslKeyFile=../../static/certChain/devkey.pem
#证书文件
#sslCertFile=../../static/certChain/devcert.pem
#最大请求长度
maxRequestSize=500000
#最大多包大小
maxMultiPartSize=10000000

[templates]
path=templates
suffix=.tpl
encoding=UTF-8
cacheSize=1000000
cacheTime=60000

[docroot]
#页面静态内容位置
path=../../static
#编码
encoding=UTF-8
#cookie存活时间
maxAge=60000
#缓存保持时间
cacheTime=60000
#缓存大小
cacheSize=1000000
#最大单个缓存文件大小
maxCachedFileSize=65536

[sessions]
#session超时时间
expirationTime=600000
#默认cookie名称
cookieName=sessionid
#默认cookie地址
cookiePath=/
#默认cookie说明
cookieComment=Identifies the user
;cookieDomain=stefanfrings.de

[logging]
; The logging settings become effective after you comment in the related lines of code in main.cpp.
#log输出路径
fileName=../../logs/webRuntime.log
#最小输出等级(0=DEBUG, 1=WARNING, 2=CRITICAL, 3=FATAL, 4=INFO)
minLevel=1
#缓冲区大小
bufferSize=100
#日志文件大小
maxSize=1000000
#日志最大备份数量
maxBackups=2
#日志时间格式
timestampFormat=yyyy-MM-dd hh:mm:ss.zzz
;msgFormat={timestamp} {typeNr} {type} {thread} {msg}
#日志内容格式
msgFormat={timestamp} {typeNr} {type} {thread} {msg}\n  in {file} line {line} function {function}
; QT5 supports: msgFormat={timestamp} {typeNr} {type} {thread} {msg}\n  in {file} line {line} function {function}


[socketserver]
host=127.0.0.1
port=8160

记得把文件放在程序目录下

编译运行启动程序,监听端口

然后用浏览器请求这个服务器,获取想要的内容

这个多线程好用的httpserver就弄好了,不怎么懂的,可以断点调试,慢慢理解,提升蛮大的

有什么不懂的,可以评论区留言

相关推荐
程序员-King.1 天前
【Qt开源项目】— ModbusScope-day 4
开发语言·qt
程序员-King.1 天前
【Qt开源项目】— ModbusScope-day 5
开发语言·qt
老秦包你会1 天前
QT第五课------QT系统相关------线程
开发语言·qt
淼淼7631 天前
Qt拖动工具栏控件到图页中均匀展示
开发语言·c++·windows·qt
YouEmbedded1 天前
解码信号与槽(含 QTimer 应用)
qt·定时器·信号与槽
小灰灰搞电子1 天前
Qt SCXML 模块详解
开发语言·qt
开始了码1 天前
UDP 协议详解与 Qt 实战应用
qt·网络协议·udp
深蓝海拓2 天前
PySide6从0开始学习的笔记(三) 布局管理器与尺寸策略
笔记·python·qt·学习·pyqt
꧁坚持很酷꧂2 天前
Windows安装Qt Creator5.15.2(图文详解)
开发语言·windows·qt
淼淼7632 天前
QT表格与数据
开发语言·qt