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

server/2024/9/23 18:50:01/

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接口的内容

#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
#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类,用来做请求映射的管理,也就是对于每一个不同类型接口,可以根据定义好的类型来做出相应的处理,内容如下:

#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
#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/WebApi1HttpRequestHandler *handler = m_requestMap.value(pathList.value(2));if (handler != nullptr) {handler->service(request, response);} else {this->returnError(response);}
}
在建立一个WebApiManager的管理类,内容如下
#ifndef WEBAPIMANAGER_H
#define WEBAPIMANAGER_H#include <QObject>class WebApiManager: public QObject
{
public:WebApiManager(QObject *parent = nullptr);void init();
};#endif // WEBAPIMANAGER_H
#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 storeQSettings *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 listenerQSettings *listenerSettings = new QSettings(configFileName, QSettings::IniFormat, this);listenerSettings->beginGroup("listener");new HttpListener(listenerSettings, new RequestMapper(this), this);
}

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

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

[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就弄好了,不怎么懂的,可以断点调试,慢慢理解,提升蛮大的

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


http://www.ppmy.cn/server/32408.html

相关文章

安卓手机APP开发__媒体开发部分__分享声音的输入

安卓手机APP开发__媒体开发部分__分享声音的输入 目录 概述 安卓10之前的版本的行为 安卓10的行为 共享场景 小助手普通的APP 有可读取权的服务 普通的APP 两个普通的APP 语音电话 普通的APP 概述 声音的输入通常来自于内嵌的麦克风,还有外置的麦克网,或者是一个…

vue key的原理和作用

1、虚拟dom中key的作用&#xff1a; key是虚拟dom对象的标识&#xff0c;当状态中的数据发生变化时&#xff0c;vue会根据新数据生成新的虚拟DOM&#xff0c;随后vue进行新虚拟dom和旧虚拟dom的差异对比。 2、对比规则&#xff1a; 1&#xff09;旧虚拟dom中找到了与新虚拟dom…

保序加密技术:保护数据有序性的安全方案

在数据安全领域&#xff0c;除了常见的保密性、完整性和可用性需求外&#xff0c;某些特定场景还需要保护数据的有序性。保序加密技术&#xff08;Order Preserving Encryption, OPE&#xff09;就是为了满足这一需求而设计的。本文将介绍保序加密技术的基本原理、应用场景以及…

数据结构––广义表

1.什么是广义表 广义表:由n&#xff08; 0)个表元素组成的有限序列: LS (ao, at, a2...an-&#xff09; LS是广义表的名称 a是广义表的元素&#xff0c;既可以是表(称为子表) 也可以是数据元素(称为原子) n为广义表的长度&#xff08;n0的广义表为空表) 2.广义表的深度和长度 …

腾讯云IM即时通信引入(React Web端组件式)

开发环境要求 React ≥ v18.0 &#xff08;17.x 版本不支持&#xff09; TypeScript node&#xff08;12.13.0 ≤ node 版本 ≤ 17.0.0, 推荐使用 Node.js 官方 LTS 版本 16.17.0&#xff09; npm&#xff08;版本请与 node 版本匹配&#xff09; chat-uikit-react 集成 …

Cube MX的多通道ADC DMA配置用于matlab的自动代码生成。

网络文章的参考。 ADC-多通道采集(DMA) https://bbs.21ic.com/icview-1596444-1-1.html &#xff1a;数据宽度为字Word&#xff0c;连续转换模式-使能&#xff0c;不连续转换-禁止。STM32CubeMX教程14 ADC - 多通道DMA转换 https://blog.csdn.net/lc_guo/article/details/1353…

【算法】规划兼职工作 线性dp+二分

题目信息 你打算利用空闲时间来做兼职工作赚些零花钱。 这里有 n 份兼职工作&#xff0c;每份工作预计从 startTime[i] 开始到 endTime[i] 结束&#xff0c;报酬为 profit[i]。 给你一份兼职工作表&#xff0c;包含开始时间 startTime&#xff0c;结束时间 endTime 和预计报…

13.1 QQ邮箱

1. 邮箱发送 2. 准备工作 3. 整合SpringBoot 3.1 配置 依赖引入 <!-- 邮件服务--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>application.…