使用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);

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

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

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);
            }
        }
    }
}

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");
}

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);
        }
    }
}

上述代码,可以看到,我的数据是存放在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;
}

三、总结

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

  • 7
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

敲代码的雪糕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值