在QT中搭建轻量级HTTP服务器

7/5/2023 网络开发

在软件开发过程中,我们离不开软件之间的相互通信。最常见的通信就是网站页面操作和后台数据间的交互,例如在登录网页版邮箱时,输入用户名和密码后,单击登录按钮将会触发和后台的通信。 今天本文要讲的是在 qt 项目中如何使用 http 服务器,当我我们大部分是使用 spring boot 搭建 http 服务器应用。但对于大部分 qt 项目而言,一个应用程序的大小都不超过 10mb,如果想在应用本地启动一个 http 的服务器,直接使用 spring boot 的话,本地电脑将会按照 java 运行环境以及创建 spring boot 应用,这将导致软件非常繁琐。 可能有人会问具体场景是什么?例如数策拍牌软件在做多标书版本时,用户直接想看当前投标进度。这时我们可用利用 qt 创建一个 http 服务器,用户可用通过网页获取投标进度,而不是将本地数据发送到原创服务器,然后再通过网页访问远程数。 这样就兼顾了本地 windows 应用程序,以及本地数据进行网页展示、访问的需求。

image-20230705230722393

# 一、http 服务器实现选型

在为 qt 项目选择 http 服务器框架时,因为安装和软件包原因,就放弃了优秀的 spring boot 作为本地 http 服务器的方案。通过网络搜索发现有 qthttpserver、cpp-httplib、QHttpEngine、QtWebApp 等开源框架。 1、QtHttpServer 当我们用过 python 的 Flask 框架,就会发现 QtHttpServer 也是借鉴了类似理念,直接使用 server.route(“url”)就可以将一个 url 和处理函数进行绑定,使用起来非常方便。可以讲用过 Flask 后肯定会选择 QtHttpServer 这个框架,但这个框架在使用时不仅需要编译,而且已经 3 年没有更新了。 2、cpp-httplib 这个框架没有仔细研究,为什么?虽然这也是一个非常轻量级的 http 应用框架,但这个框架会阻塞主进程,虽然可以开一个子线程,让 listen 函数在子线程中执行,就不会阻塞主线程,但这有增加了软件的复杂性。 3、QHttpEngine QHttpEngine 也可以开发 HTTP 服务器应用程序,但该框架也已经 6 年多没有更新。 4、QtWebApp QtWebApp 给人的初始感觉就是小巧,小到我觉得是一个草根框架。但自己研究后发现的确是“大道至简”,因为该框架包括我 spring boot 框架的常见功能,而且最近半年还在更新。 所以就选择将局域网服务的 http server 换成 QtWebApp。

# 二、什么是 QtWebAPP

QtWebApp 就是利用 qt 实现的、能够使用 C++中开发 HTTP Web 服务器应用程序的 httpserver 库。 我们在局域网内的 http 应用主要解决轻量级网页展示和交互,而 QtWebApp 库提供了创建基于 Web 的界面就直接涵盖了这些功能。

  • 网页模板化,可以通过 html 网页模板,向用户展示动态内容的页面。
  • 访问授权,可以使用 cookies、session 机制,实现 web 应用的访问授权。
  • http/https 接口,可以实现请求 url 和 controller 的映射,只需要创建 HttpRequestHandler 的子类,就可以实现自己的业务逻辑。 QtWepApp 本身就是 qt 编写,所以可以直接复制该框架代码到软件项目中,实现和现有 qt 项目的快速集成。也正因为是是 Qt 写的,所以有跨平台的支持。 QtVWebApp 包含如下的组成部分:
  • HTTP Server:HTTP 服务器以并行的线程处理请求。支持 IPv4 以及 IPv6,持久连接,HTTPS, seesion, cookie 和文件上传。
  • Template Engine:模板引擎用于基于模板文件,例如 html 文件,可以直接对纯文本的搜索/替换操作,然后通过 http 服务返回动态页面。
  • File Logger:日志插用于写日志文件,他还具有丰富的附加属性,如时间戳,线程 ID, Session ID 和其他信息。对日志程序配置文件的更改将在没有程序重启的情况下自动激活。 上面这些功能模块,在 QtWebAPP 直接对应一个代码文件夹,而且这些模块是可以相互独立运行,所以我们只需要复制需要的模块到自己的 qt 工程就可以使用了。

# 三、基于 QtWebAPP 创建 http 服务器

如果大家用过 spring boot 创建一个 web 应用,脑海里面的步骤就是创建 spring boot 脚手架、controller 和业务 service、数据库访问 DAO。而我们在使用 QtWebApp 时也时这个实现步骤。 1、创建项目工程 通过 qt 开发环境创建一个 qt 工程,然后将 QtWebAPP 的模块按需复制到 qt 工程中,例如最常用的就是复制 httpserver 文件夹,这里需要注意的是复制整个 httpserver 文件夹。 2、创建 mapper 和 controller 对象 我们在 spring boot 中使用@RequestMapping 捕获数据请求,并且绑定到 controller 的一个函数,例如:

@RequestMapping(value = "/test",produces="application/json")
//构造方法 这个方法是UserController类中的一个方法,跟mapper类没有关系
public String getUserRequest(@RequestBody String jsonString){
    return jsonString;
}
1
2
3
4
5

而在 QtWebAPP 中,我们只要创建一个 HttpRequestHandler 的 mapper 类,以及若干个 HttpRequestHandler 的 congtroller 类,然后通过 mapper 实现不同 controller 的转发就可以完成不同 url 的操作。

void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
    QByteArray path=request.getPath();
    ...
    else if (path=="/list2") {
        dataTemplateController.service(request, response);
    }
    else if (path.startsWith("/files")) {
        staticFileController->service(request,response);
    }
    ...

    qDebug("RequestMapper: finished request");
}
1
2
3
4
5
6
7
8
9
10
11
12
13

3、数据访问操作 因为我们主要是针对本地创建的 http 应用,所以我们在 controller 中一般使用会和本地 qt 应用进行交互,所以数据一般都会存储到 qt 应用软件中,也就是存在复杂的数据库访问操作。

# 四、参数配置

既然是一个 http 应用,当然离不开缓存大小、线程数量等设置,这些配置会通过一个 ini 文件进行约定,然后通过 QSettings 进行读取。

QSettings* listenerSettings=
         new QSettings("../MyFirstWebApp/etc/webapp1.ini",QSettings::IniFormat,&app);
1
2

ini 文件内容如下,这些设置可以讲和 spring boot 的设置非常类似:

[listener]
;host=192.168.0.100
port=2386
minThreads=4
maxThreads=100
cleanupInterval=60000
readTimeout=60000
maxRequestSize=16000
maxMultiPartSize=10000000
1
2
3
4
5
6
7
8
9

# 五、总结

当我们在软件开发过程中,软件是解决用户的实际痛点问题,而软件框架就是解决我们开发人员的实际问题。就像有若干个 http 应用框架时,我们还是根据实际框选择不同的软件实现方式。