• 使用QtWebApp搭建Http服务器



    前言

      本篇介绍如何使用QtWebApp搭建一个win环境下的http服务器。

    一、QtWebApp

    QtWebApp教程如下:

    1. http://stefanfrings.de/qtwebapp/tutorial/index.html#part2
    2. https://showroom.qt.io/qtwebapp/

    在第一个链接中,可以下载最新版的QtWebApp,目录如下:
    在这里插入图片描述

    二、使用

    1、将QtWebApp的源码拷贝到qt工程中

    在这里插入图片描述

    2、配置文件

    根据需要修改webapp.ini配置文件:
    在这里插入图片描述

      配置文件中各参数解释:

    1. host和post:代表web服务器的IP地址和端口。公用Web服务器使用端口80,而内部Web服务器通常在端口8080上侦听。
    2. minThreads:代表始终保持运行的线程数量,用来确保一段时间不活动后的良好响应时间。
    3. maxThreads:QtWebApp可以同时处理多个http请求,该参数指定并发工作线程的最大数量。其值要根据机器性能而定(可以利用负载生成器等工具来确定)。
    4. cleanupInterval:Web服务器始终以空线程池开头,当HTTP请求进入时,将根据需要创建线程。空闲线程由计时器缓慢关闭。每隔一个cleanupInterval时间间隔(以毫秒为单位),服务器都将关闭一个空闲线程。
    5. readTimeout:设置通过打开大量连接而不使用它们,来保护服务器免受简单的拒绝服务攻击。静默连接将在设定的毫秒数后被关闭。通常情况下,是由Web浏览器来关闭连接。
    6. maxRequestSize:保护服务器免受非常多的HTTP请求而导致内存过载的影响。此值适用于常规请求。
    7. maxMultiPartSize:适用于网络浏览器将文件上传到服务器时发生的大部分请求。如果要接受10 MB的文件,由于HTTP协议开销,必须将此值设置得更大一些。
    8. 后面注释的配置:用于openssl认证(实现https请求)

    3、创建http侦听器

    new stefanfrings::HttpListener(listenerSettings,new stefanfrings::StaticFileController(listenerSettings), this);
    
    • 1

    new stefanfrings::HttpListener(listenerSettings, new RequestMapper(), this);
    
    • 1

    参数可以是一个单独的控制器,也可以是一个请求映射器。

    4、控制器类的实现

    这里实现一个文件下载的控制器类,重点代码如下:

    void StaticFileController::service(HttpRequest &request, HttpResponse &response)
    {
        QByteArray path=request.getPath();
        qDebug()<<"Tami path ="<<path;
        QByteArray parameter = request.getParameter("fileName");
        qDebug()<<"Tami parameter ="<<parameter;
    
        // Check if we have the file in cache
        qint64 now=QDateTime::currentMSecsSinceEpoch();
        mutex.lock();
        CacheEntry* entry = cache.object(path);
        if (entry && (cacheTimeout==0 || entry->created > now-cacheTimeout))
        {
            QByteArray document = entry->document; //copy the cached document, because other threads may destroy the cached entry immediately after mutex unlock.
            QByteArray filename = entry->filename;
            qDebug()<<"Tami filename ="<<filename;
            mutex.unlock();
            qDebug("StaticFileController: Cache hit for %s",path.data());
            setContentType(filename,response);
            response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000));
            response.write(document,true);
        }
        else
        {
            mutex.unlock();
            // The file is not in cache.
            qDebug("StaticFileController: Cache miss for %s",path.data());
            // Forbid access to files outside the docroot directory
            if (path.contains("/.."))
            {
                qWarning("StaticFileController: detected forbidden characters in path %s",path.data());
                response.setStatus(403,"forbidden");
                response.write("403 forbidden",true);
                return;
            }
            // If the filename is a directory, append index.html.
            if (QFileInfo(docroot+"/"+parameter/*path*/).isDir())
            {
                qDebug()<<"Tami 111111111";
                path+="/index.html";
            }
            // Try to open the file
            QFile file(docroot+"/"+parameter/*path*/);
            qDebug("StaticFileController: Open file %s",qPrintable(file.fileName()));
            if (file.open(QIODevice::ReadOnly))
            {
                setContentType(parameter,response);
                response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000));
                response.setHeader("Content-Length",QByteArray::number(file.size()));
                if (file.size() <= maxCachedFileSize)
                {
                    // Return the file content and store it also in the cache
                    entry=new CacheEntry();
                    while (!file.atEnd() && !file.error())
                    {
                        QByteArray buffer=file.read(65536);
                        response.write(buffer);
                        entry->document.append(buffer);
                    }
                    entry->created=now;
                    entry->filename=path;
                    mutex.lock();
                    cache.insert(request.getPath(),entry,entry->document.size());
                    mutex.unlock();
                }
                else
                {
                    // Return the file content, do not store in cache
                    while (!file.atEnd() && !file.error())
                    {
                        response.write(file.read(65536));
                    }
                }
                file.close();
            }
            else {
                if (file.exists())
                {
                    qWarning("StaticFileController: Cannot open existing file %s for reading",qPrintable(file.fileName()));
                    response.setStatus(403,"forbidden");
                    response.write("403 forbidden",true);
                }
                else
                {
                    response.setStatus(404,"not found");
                    response.write("404 not found",true);
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    5、请求映射器类的实现

    void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
        QByteArray path=request.getPath();
        qDebug("RequestMapper: path=%s",path.data());
    
        if (path=="/" || path=="/hello") {
            HelloWorldController().service(request, response);
        }
        else if (path=="/file") {
            StaticFileController(listenerSettings).service(request, response);
        }
        else if(path == "/doctor")
        {
            DoctorController().service(request, response);
        }
        else if(path == "/drug")
        {
            DrugController().service(request, response);
        }
        else {
            response.setStatus(404,"Not found");
            response.write("The URL is wrong, no such document.");
        }
    
        qDebug("RequestMapper: finished request");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    6、浏览器请求

    实现了一个http服务器后,就可以通过浏览器进行http请求,如下:
    在这里插入图片描述
    当收到http请求后,请求映射器类进行分类处理,具体实现依然在控制器类中实现。比如,上面的值班医生信息请求,具体实现如下:

    void DoctorController::service(HttpRequest &request, HttpResponse &response)
    {
        //response.write("Hello World",true);
        QByteArray path=request.getPath();
        qDebug()<<"Tami path ="<<path;
        QByteArray parameter = request.getParameter("date");
        qDebug()<<"Tami parameter ="<<parameter;
    
        // Check if we have the file in cache
        qint64 now=QDateTime::currentMSecsSinceEpoch();
        mutex.lock();
        CacheEntry* entry = cache.object(path);
        if (entry && (cacheTimeout == 0 || entry->created > now-cacheTimeout))
        {
            QByteArray document = entry->document; //copy the cached document, because other threads may destroy the cached entry immediately after mutex unlock.
            QByteArray filename = entry->filename;
            qDebug()<<"Tami filename ="<<filename;
            mutex.unlock();
            qDebug("DoctorController: Cache hit for %s",path.data());
            setContentType(filename,response);
            response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000));
            response.write(document,true);
        }
        else
        {
            mutex.unlock();
            // The file is not in cache.
            qDebug("DoctorController: Cache miss for %s",path.data());
            // Forbid access to files outside the docroot directory
            if (path.contains("/.."))
            {
                qWarning("DoctorController: detected forbidden characters in path %s",path.data());
                response.setStatus(403,"forbidden");
                response.write("403 forbidden",true);
                return;
            }
            //
            QByteArray array = g_pDataBase->selectDoctorData(QString(parameter));
            if(array.isEmpty())
            {
                response.setStatus(404,"not found");
                response.write("404 not found",true);
            }
            else
            {
                setContentType(".json",response);
                response.write(array);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    上述代码,可以看到,我的数据是存放在mysql中的,数据库查询代码如下:

    QByteArray Tmdatabase::selectDoctorData(QString date)
    {
        QString strSelectData;
        strSelectData.clear();
        //
        strSelectData = QString("select * from doctor where scheduleDate='%1'").arg(date);
        //
        QSqlQuery    m_doctorQuery(database);
        m_doctorQuery.prepare(strSelectData);
        //
        QJsonObject json;
        if(!m_doctorQuery.exec())
        {
            qDebug()<<m_doctorQuery.lastError();
        }
        else
        {
            //
            json.insert("status",0);
            json.insert("errorMsg","");
            //
            QJsonArray jsonArray;
            while(m_doctorQuery.next())
            {
                //
                QJsonObject jsonItem;
                jsonItem.insert("deptName",m_doctorQuery.value(0).toString());
                jsonItem.insert("doctorName",m_doctorQuery.value(1).toString());
                jsonItem.insert("scheduleDate",m_doctorQuery.value(2).toString());
                jsonItem.insert("periodStart",m_doctorQuery.value(3).toString());
                jsonItem.insert("periodEnd",m_doctorQuery.value(4).toString());
                jsonItem.insert("classes",m_doctorQuery.value(5).toString());
                jsonItem.insert("fee",m_doctorQuery.value(6).toString());
                jsonItem.insert("remain",m_doctorQuery.value(7).toString());
                jsonArray.append(jsonItem);
            }
            json.insert("data",QJsonValue(jsonArray));
        }
        m_doctorQuery.finish();
        //
        QJsonDocument document;
        document.setObject(json);
        QByteArray byteArray = document.toJson(QJsonDocument::Compact);
        //QString strJson(byteArray);
        return byteArray;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    三、总结

    至于https请求,具体实现没有研究,感兴趣的可以尝试一下。

  • 相关阅读:
    《你好,C语言》:从另一个视角学习并重新审视C语言的意义
    SpringBoot系统搭建集成-RabbitMq延时队列
    代码随想录算法训练营第三十六天| 435. 无重叠区间 763.划分字母区间 56. 合并区间
    React-Admin后台管理模板|react18+arco+zustand后台解决方案
    openresty+etcd配置实现原理
    Unity之ShaderGraph如何实现无贴图水球效果
    【云原生 | Kubernetes 系列】---altermanager消息配置和pushgateway
    LoadRunner调用Analysis组件的相关技术点及知识总结
    NLP基础——Bag of Words 词袋法
    Linux内核中网络部分结构以及分布
  • 原文地址:https://blog.csdn.net/u011832219/article/details/126269051