前言
本篇介绍如何使用QtWebApp搭建一个win环境下的http服务器。
一、QtWebApp
QtWebApp教程如下:
在第一个链接中,可以下载最新版的QtWebApp,目录如下:
二、使用
1、将QtWebApp的源码拷贝到qt工程中
2、配置文件
根据需要修改webapp.ini配置文件:
配置文件中各参数解释:
- host和post:代表web服务器的IP地址和端口。公用Web服务器使用端口80,而内部Web服务器通常在端口8080上侦听。
- minThreads:代表始终保持运行的线程数量,用来确保一段时间不活动后的良好响应时间。
- maxThreads:QtWebApp可以同时处理多个http请求,该参数指定并发工作线程的最大数量。其值要根据机器性能而定(可以利用负载生成器等工具来确定)。
- cleanupInterval:Web服务器始终以空线程池开头,当HTTP请求进入时,将根据需要创建线程。空闲线程由计时器缓慢关闭。每隔一个cleanupInterval时间间隔(以毫秒为单位),服务器都将关闭一个空闲线程。
- readTimeout:设置通过打开大量连接而不使用它们,来保护服务器免受简单的拒绝服务攻击。静默连接将在设定的毫秒数后被关闭。通常情况下,是由Web浏览器来关闭连接。
- maxRequestSize:保护服务器免受非常多的HTTP请求而导致内存过载的影响。此值适用于常规请求。
- maxMultiPartSize:适用于网络浏览器将文件上传到服务器时发生的大部分请求。如果要接受10 MB的文件,由于HTTP协议开销,必须将此值设置得更大一些。
- 后面注释的配置:用于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请求,具体实现没有研究,感兴趣的可以尝试一下。