Qt+QtWebApp开发笔记(一):QtWebApp介绍/下载和搭建基础封装http轻量级服务器Demo

科技资讯 投稿 24100 0 评论

Qt+QtWebApp开发笔记(一):QtWebApp介绍/下载和搭建基础封装http轻量级服务器Demo

前言

  本篇实战解说QtWebApp的轻量级Demo。
  本篇篇幅较长,为了保持基础的完整性将必要的东西都放在本篇。

 

Demo

下载地址

 

QtWebApp(HTTP Server in C++)

概述

  QtWepApp是一个C++中的HTTP服务器库,其灵感来自Java Servlet。适用于Linux、Windows、Mac OS和Qt Framework支持的许多其他操作系统。
  QtWebApp包含以下组件:

    HTTP(S1.0和1.1服务器
  • 模板引擎
  • 缓冲记录器
      这些组件可以相互独立地使用。一个非常小的用法示例:
// The main program starts the HTTP server
int main(int argc, char *argv[]
{
    QCoreApplication app(argc,argv;
        
    new HttpListener(
        new QSettings("configfile.ini", QSettings::IniFormat, &app,
        new MyRequestHandler(&app,
        &app;

    return app.exec(;
}


// The request handler receives and responds HTTP requests
void MyRequestHandler::service(HttpRequest& request, HttpResponse& response
{
    // Get a request parameters
    QByteArray username=request.getParameter("username";

    // Set a response header
    response.setHeader("Content-Type", "text/html; charset=UTF-8";

    // Generate the HTML document
    response.write("<html><body>";
    response.write("Hello ";
    response.write(username;
    response.write("</body></html>";
}

  大约2MB的小内存需求使web服务器有资格用于嵌入式系统(PS:非常符合后续arm产品定位)。对于更大的网络服务来说,它也足够强大。
  记录器通过将调试消息保留在内存中直到出现错误来提高磁盘空间和性能。只要一切正常,就不会编写调试消息。
  对记录器配置的更改将自动变为活动状态,而无需重新启动程序。
  该库使用Qt 4.7至6.x版本运行。如果是Qt 6,则需要安装Qt5Compat库。它包含对许多8位字符编码的支持,Qt6默认情况下不再支持这些编码。可以在LGPL许可证的条件下使用该软件。
作者项目的起源
  多年前,一位经验丰富的Java开发人员坚持认为Java是互联网语言,因为在其他编程语言中进行互联网通信要复杂得多。这不正确,但拒绝相信。所以开始挑战。
  任务:与Qt4相比,仅使用Java 6运行时库对具有一些基本功能的HTTP服务器进行编程。
两个项目都很相似,实现了任务。同时,Qt/C++程序比Java版本小得多,也快得多。
  几年后,用这个原型制作了一个库,并将其用于一些私人项目。大学鼓励发布代码。从那以后,再也不用这个项目了,但还是进行了一些改进,让人们感到高兴。
  有趣的是,Qt制造商多年来一直在开发标准HTTP服务器,但到2022年,它仍然不包括在Qt库中。这也许可以解释为什么很多人使用的库。

QtWebApp下载地址

  链接:https://pan.baidu.com/s/1v9DTrajX8Mv-xnhScDhN8g?pwd=1234

编写Web服务器应用程序环境

Windows

  安装好Qt,下载QtWebApp源码;

Linux

    Debian, Ubuntu
sudo apt install build-essential gdb libgl1-mesa-dev
    Fedora, RedHat, CentOS sudo
yum groupDebian, Ubuntunstall "C Development Tools and Libraries"
sudo yum install mesa-libGL-devel
    openSUSE
sudo zypper install -t pattern devel_basis

运行下载的Demo(这里是ubuntu环境)

  为的编程项目创建一个目录,然后在那里下载并提取QtWebApp ZIP文件。启动QT Creator IDE并打开项目文件Demo1/Demo1.pro。这将打开“配置项目”对话框:
  

  

  
  如果在屏幕上看到那个网站,所有需要的软件都能正常工作。现在可以关闭演示应用程序。

如何使用QtWebApp

在自己的程序中构建

  提取编程文件夹中的QtWebApp.zip文件,并创建一个名为“MyFirstWebApp”的新Qt控制台项目(如果尚未完成)。然后,应该拥有与相同的文件夹结构:
  

QT += network
include(../QtWebApp/QtWebApp/httpserver/httpserver.pri

  第一行激活Qt的网络模块,第二行包括QtWebApp的HTTP服务器模块的源代码。因此,当编译程序时,HTTP服务器将成为可执行文件的一部分。
  
  作为替代方案,可以使用共享库。要生成它,请打开项目QtWebApp/QtWebApp-QtWebApp.pro并构建它。然后查看QtWebApp/Demo2/Demo2.pro,了解如何链接到共享库。然而,建议包含如上所示的源代码,因为这样不太容易出错。

配置参数

[listener]
;host=192.168.0.100
port=8080
minThreads=4
maxThreads=100
cleanupInterval=60000
readTimeout=60000
maxRequestSize=16000
maxMultiPartSize=10000000

  主机和端口参数指定web服务器侦听的IP地址和端口。如果注释掉主机(如上所述),则服务器将侦听所有网络接口。公共web服务器使用端口80,而内部web服务器通常在端口8080上侦听。可以使用任何喜欢的自由端口。
  Unix用户应该记住,1024以下的端口号是为“root”用户保留的。Windows用户可能需要配置Windows防火墙以允许从其他计算机进行访问。
  QtWebApp可以同时处理多个HTTP请求,因此它是多线程的。由于启动一个新线程需要花费大量时间,QtWebApp会将线程重新用于后续的HTTP请求。
  maxThreads值指定并发工作线程的最大数量。在进入生产环境之前,应该使用负载生成器工具来了解服务器在不耗尽内存或变得迟缓的情况下可以处理多少负载。
  minThreads空闲时并发工作线程的最小数量。web服务器总是以一个空线程池开始。线程是在HTTP请求传入时按需创建的。空闲线程由计时器缓慢关闭。每个cleanupInterval(以毫秒为单位),服务器都会关闭一个空闲线程,但是minThreads的数量总是保持运行。使用给定的值,的服务器最多可以处理100个并发HTTP连接。它保持4个空闲的工作线程运行,以确保良好的响应时间。
  readTimeout设置通过打开大量连接而不使用这些连接来保护服务器免受简单的拒绝服务攻击的超时时间。空闲连接在该毫秒数之后关闭。在正常情况下,网络浏览器负责关闭连接。
  maxRequestSize保护服务器不受非常大的HTTP请求造成的内存过载的影响。此值适用于常规请求。
  maxMultiPartSize值适用于web浏览器将文件上载到服务器时发生的多部件请求。如果想接收10兆字节的文件,由于HTTP协议开销,必须将此值设置得稍大一些。
  上传的文件存储在临时文件中。临时文件夹的位置由操作系统定义。
  继续创建的第一个web应用程序。要使此配置文件在Qt Creator中可见,请在项目文件中添加一行:

OTHER_FILES += etc/webapp1.ini

  现在添加一些代码来加载该文件:

#include <QCoreApplication>
#include <QSettings>

int main(int argc, char *argv[]
{
    QCoreApplication app(argc, argv;

    QSettings* listenerSettings=
         new QSettings("/home/sfrings/programming/MyFirstWebApp/etc/webapp1.ini",QSettings::IniFormat,&app;
    qDebug("config file loaded";

    return app.exec(;
}

  但更喜欢在几个文件夹中自动搜索配置文件,这样就可以在IDE内外运行应用程序,而无需更改路径:

#include <QCoreApplication>
#include <QSettings>
#include <QFile>
#include <QDir>
#include <QString>

/**
 * Search the configuration file.
 * Aborts the application if not found.
 * @return The valid filename
 */
QString searchConfigFile( {
    QString binDir=QCoreApplication::applicationDirPath(;
    QString appName=QCoreApplication::applicationName(;
    QString fileName("Demo1.ini";

    QStringList searchList;
    searchList.append(binDir;
    searchList.append(binDir+"/etc";
    searchList.append(binDir+"/../etc";
    searchList.append(binDir+"/../"+appName+"/etc";     // for development with shadow build (Linux
    searchList.append(binDir+"/../../"+appName+"/etc";  // for development with shadow build (Windows
    searchList.append(QDir::rootPath(+"etc/opt";
    searchList.append(QDir::rootPath(+"etc";

    foreach (QString dir, searchList
    {
        QFile file(dir+"/"+fileName;
        if (file.exists(
        {
            fileName=QDir(file.fileName(.canonicalPath(;
            qDebug("Using config file %s",qPrintable(fileName;
            return fileName;
        }
    }

    // not found
    foreach (QString dir, searchList
    {
        qWarning("%s/%s not found",qPrintable(dir,qPrintable(fileName;
    }
    qFatal("Cannot find config file %s",qPrintable(fileName;
    return nullptr;
}


int main(int argc, char *argv[]
{
    QCoreApplication app(argc, argv;

    // Load the configuration file
    QString configFileName=searchConfigFile(;
    QSettings* listenerSettings=new QSettings(configFileName, QSettings::IniFormat, &app;
    qDebug("config file loaded";

    return app.exec(;
}

  过程searchConfigFile(在多个文件夹中搜索文件。
  方法**QDir::canonicalPath(将相对路径名转换为绝对形式,这在下面的调试消息中看起来更好。
  如果找不到该文件,则应用程序会输出一条带有qFatal(**的错误消息,这也会中止程序。
一旦加载了配置文件,就可以创建一个HTTP侦听器对象,它是web服务器的核心:

#include <QCoreApplication>
#include <QSettings>
#include <QFile>
#include <QDir>
#include <QString>
#include "httplistener.h"
#include "httprequesthandler.h"

using namespace stefanfrings;

int main(int argc, char *argv[]
{
    QCoreApplication app(argc, argv;
    
    // Load the configuration file
    QString configFileName=searchConfigFile(;    
    QSettings* listenerSettings=new QSettings(configFileName, QSettings::IniFormat, &app;
    listenerSettings->beginGroup("listener";
    
    // Start the HTTP server
    new HttpListener(listenerSettings, new HttpRequestHandler(&app, &app;

    return app.exec(;
}

  方法**QSettings::beginGroup(**从配置文件中选择组“[listener]”。稍后将添加更多组。
  HttpRequestHandler接收所有传入的HTTP请求,并生成响应。默认情况下,请求处理程序只返回一个错误页面。
  在堆上用“new”创建HttpListener是很重要的,否则它将在程序启动后立即终止。
运行程序并打开URLhttp://localhost:8080在web浏览器中。将在控制台窗口中收到错误页面“501未实现”和调试消息。
  
  这是很多会减慢程序速度的消息,但它们对调试很有帮助。在Qt Creator的左边框中,可以通过单击紫色按钮将构建模式从“调试”更改为“发布”。发布版本不那么冗长:
  

编写自己的请求处理程序

  为了输出“Hello World”消息,必须编写自己的请求处理程序。用鼠标右键单击src文件夹,选择“添加新…”,然后选择“C++类”。
  

helloworldcontroller.h:

#ifndef HELLOWORLDCONTROLLER_H
#define HELLOWORLDCONTROLLER_H

#include "httprequesthandler.h"

using namespace stefanfrings;

class HelloWorldController : public HttpRequestHandler {
    Q_OBJECT
public:
    HelloWorldController(QObject* parent=0;
    void service(HttpRequest& request, HttpResponse& response;
};

#endif // HELLOWORLDCONTROLLER_H

helloworldcontroller.cpp:

#include "helloworldcontroller.h"

HelloWorldController::HelloWorldController(QObject* parent
    : HttpRequestHandler(parent {
    // empty
}

void HelloWorldController::service(HttpRequest &request, HttpResponse &response {
    response.write("Hello World",true;
}

  可选参数“true”表示这是当前HTTP请求的最后一次write(调用。
main.cpp中的两个更改:

#include "helloworldcontroller.h"

    new HttpListener(listenerSettings,new HelloWorldController(&app,&app;

  运行程序并打开URLhttp://localhost:8080在网络浏览器中。
  

 

Qt的http服务Demo搭建流程(windows)

步骤一:下载QtWebApp

略;

步骤二:新建工程testHttpDemo

步骤三:拷贝http

  

  httpserver模块,QtWebApp自带的三方模块

# httpserver模块,QtWebApp自带的三方模块
include ($$PWD/modules/httpserver/httpserver.pri

  

步骤四:自建http管理类用模块化

  再建立一个http管理类来处理,如下:
  
  

  

步骤五:写一个Hello world的展示消息处理

  继承HttpRequestHandler消息处理类,开始新建一个类:
  

  

  

  
  

步骤六:在http运行管理类中启用这个监听

  

  
  这里已经将http的轻量级服务器已经子线程模块化融入带界面的qt应用中(带不带界面融入过程都一样,只是QApplication和QCoreApplication以及在哪初始化的问题了)

步骤七:测试

  然后测试打开成功:
  

步骤八:编码一刀切处理

  我们忽略系统编码,统一进行utf-8进行转换,避免因为系统问题而去单独处理这个问题:
  

  

步骤九:打成运行包单独运行再测试服务器

  

 

模块化

  

 

Demo源码

HttpServerManager.h

#ifndef HTTPSERVERMANAGER_H
#define HTTPSERVERMANAGER_H

#include <QObject>
#include <QMutex>

#include "httplistener.h"
#include "HelloWorldRequestHandler.h"

class HttpServerManager : public QObject
{
    Q_OBJECT
private:
    explicit HttpServerManager(QObject *parent = 0;

public:
    static HttpServerManager *getInstance(;

public:
    QString getIp(               const;            // 服务器监听ip(若为空,则表示监听所有ip
    quint16 getPort(             const;            // 服务器监听端口
    int     getMinThreads(       const;            // 空闲最小线程数
    int     getMaxThreads(       const;            // 负载最大线程数
    int     getCleanupInterval(  const;            // 空线程清空间隔(单位:毫秒)
    int     getReadTimeout(      const;            // 保持连接空载超时时间(单位:毫秒)
    int     getMaxRequestSize(   const;            // 最大请求数
    int     getMaxMultiPartSize( const;            // 上载文件最大数(单位:字节)

public:
    void setIp(const QString &ip;                  // 服务器监听ip(若为空,则表示监听所有ip
    void setPort(const quint16 &port;              // 服务器监听端口
    void setMinThreads(int minThreads;             // 空闲最小线程数
    void setMaxThreads(int maxThreads;             // 负载最大线程数
    void setCleanupInterval(int cleanupInterval;   // 空线程清空间隔(单位:毫秒)
    void setReadTimeout(int readTimeout;           // 保持连接空载超时时间(单位:毫秒)
    void setMaxRequestSize(int value;              // 最大请求数
    void setMaxMultiPartSize(int value;            // 上载文件最大数(单位:字节)

public slots:
    void slot_start(;
    void slot_stop(;

private:
    static HttpServerManager *_pInstance;
    static QMutex _mutex;

private:
    bool _running;

private:
    HttpListener *_pHttpListener;                   // http服务监听器
    QSettings *_pSettings;                          // 配置文件

private:
    QString _ip;                // 服务器监听ip(若为空,则表示监听所有ip)
    quint16 _port;              // 服务器监听端口
    int _minThreads;            // 空闲最小线程数
    int _maxThreads;            // 负载最大线程数
    int _cleanupInterval;       // 空线程清空间隔(单位:毫秒)
    int _readTimeout;           // 保持连接空载超时时间(单位:毫秒)
    int _maxRequestSize;        // 最大请求数
    int _maxMultiPartSize;      // 上载文件最大数(单位:字节)
};
#endif // HTTPSERVERMANAGER_H

HttpServerManager.cpp

#include "HttpServerManager.h"
#include <QApplication>

#include <QDebug>
#include <QDateTime>
//#define LOG qDebug(<<__FILE__<<__LINE__
//#define LOG qDebug(<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug(<<__FILE__<<__LINE__<<QThread(::currentThread(
//#define LOG qDebug(<<__FILE__<<__LINE__<<QDateTime::currentDateTime(.toString("yyyy-MM-dd"
#define LOG qDebug(<<__FILE__<<__LINE__<<QDateTime::currentDateTime(.toString("yyyy-MM-dd hh:mm:ss:zzz"

HttpServerManager *HttpServerManager::_pInstance = 0;
QMutex HttpServerManager::_mutex;

HttpServerManager::HttpServerManager(QObject *parent
    : QObject(parent,
      _pHttpListener(0,
      _pSettings(0,
      _running(false,
      _port(8088,
      _minThreads(2,
      _maxThreads(10,
      _cleanupInterval(60000,
      _readTimeout(60000,
      _maxRequestSize(100,
      _maxMultiPartSize(1024*1024*1024
{

}

HttpServerManager *HttpServerManager::getInstance(
{
    if(!_pInstance
    {
        QMutexLocker lock(&_mutex;
        if(!_pInstance
        {
            _pInstance = new HttpServerManager(;
        }
    }
    return _pInstance;
}

void HttpServerManager::slot_start(
{
    if(_running
    {
        LOG << "It's running!!!";
        return;
    }
    _running = true;
    LOG << "Succeed to run";

    // 启动http的监听
    {
        QString httpServerPath = QString("%1/etc/httpServer.ini".arg(qApp->applicationDirPath(;
        if(!_pSettings
        {
            LOG << httpServerPath << "exit:" << QFile::exists(httpServerPath;
            _pSettings = new QSettings(httpServerPath, QSettings::IniFormat;
        }
#if 0
        if(!_ip.isEmpty(
        {
            _pSettings->setValue("host"           , _ip;  // ;在ini里面是注释了
        }
        _pSettings->setValue("port"            , _port;
        _pSettings

编程笔记 » Qt+QtWebApp开发笔记(一):QtWebApp介绍/下载和搭建基础封装http轻量级服务器Demo

赞同 (122) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽