在 C++ 应用程序中嵌入 Web 服务器






4.74/5 (30投票s)
使用 Lacewing 网络库在 C++ 应用程序中嵌入 Web 服务器
引言
本文将介绍如何使用 Lacewing 网络库将嵌入式 Web 服务器集成到您的 Win32/C++ 应用程序中。它可以用于例如:
- Web 管理 - 服务器应用程序通常有一个 Web 界面,用于远程管理 - 只需要一个 Web 浏览器即可管理服务器。
- Web 应用程序 - 用 C++ 编写的 Web 应用程序可能比 PHP 或 ASP 更快、更具可伸缩性,资源占用更少,并且无需额外安装。
背景
我决定通过 Lacewing::Server
类进行实验,编写自己的 Web 服务器类。该类设计用于通过 IO 完成端口、epoll 或 kqueue 进行扩展。
Lacewing::Webserver
很快就成为了一个强大的 HTTP 服务器,具有以下功能:
- HTTP
GET
/POST
,支持自动参数解析 - 非阻塞文件发送
- Cookie 和会话
- 多部分文件上传
- 获取/设置最后修改日期,轻松实现缓存
- 完整的 HTTPS 支持
安装库
Lacewing 可在 Windows 和多种 *nix 系统(包括 Linux、BSD、Mac OS X 甚至 Android)上运行。不过,本文最初是为 Windows 版本编写的——其他平台的代码是相同的,但安装过程会有所不同。
对于 Windows,首先需要从 liblacewing 网站下载并安装 Lacewing Win32 二进制文件。Lacewing.dll 文件需要与您的可执行文件一起分发,但出于开发便利性,您也可以将其放在 Windows\System32 文件夹(64 位系统上是 Windows\SysWOW64)中。
将包含 Lacewing.h 和 Lacewing.lib 的文件夹路径添加到 VC++ 目录中,包括头文件和库部分。在 Visual C++ 2008 中,这位于 工具 > 选项 > 项目和解决方案 > VC++ 目录
下。添加路径后,您应该可以在代码中 #include <Lacewing.h>
,这将自动链接 Lacewing.lib。
使用该类
为了让 Lacewing 中的任何类正常工作,您首先需要一个 Lacewing::EventPump
实例,它负责监听事件并将其“泵送”给感兴趣的类。
然后,您可以创建一个 Lacewing::Webserver
实例,并将事件泵传递给构造函数。
Lacewing::EventPump EventPump;
Lacewing::Webserver Webserver (EventPump);
这样就已经可以托管了,并且会显示一个空白页面。要使其显示一些有趣的内容,您需要注册一些处理程序。目前我们只关心响应 GET
请求,但还有更多可用的处理程序,用于 HEAD
、POST
、错误处理和多部分文件上传等——请参阅 Lacewing.h 中的 Lacewing::Webserver
类定义以获取完整列表。
示例文档 GET
处理程序如下所示:
void onGet (Lacewing::Webserver &Webserver, Lacewing::Webserver::Request &Request)
{
Request << "Welcome to " << Request.URL ();
}
这将输出文本“欢迎来到
”,后跟请求的 URL。要将其注册到 Lacewing,以便在收到 GET
请求时调用它,只需将其传递给 Webserver
实例的 onGet
方法即可。
初始化完成后,就可以调用 Host()
来启动 Web 服务器。如果不传递任何参数,默认端口为 80。如果您的系统上端口 80 已被占用,您可能需要使用 Host(81)
或类似的端口。
Webserver.onGet (onGet);
Webserver.Host ();
就是这样——只要 Webserver
对象保持在作用域内,您就可以打开 Web 浏览器并访问 https:///home,届时您将看到:
如果您使用的端口不是 80,则需要访问类似 https://:82/home 的地址。
处理文件
传递给 GET
处理程序的 Request
对象提供了两个函数,可以方便地从磁盘发送文件:
SendFile
- 通过文件名发送文件。文件不会加载到内存中(在 Windows 上使用TransmitFile
,在 *nix 上使用sendfile
)。GuessMimeType
- 为文件扩展名或文件名设置适当的 MIME 类型。例如,htm
文件将设置 MIME 类型text/html
,而png
文件将设置 MIME 类型image/png
。
我们可以轻松地使用这些函数来构建一个简单的 Web 服务器,托管当前目录。
void onGet(Lacewing::Webserver &Webserver, Lacewing::Webserver::Request &Request)
{
Request.GuessMimeType(Request.URL());
Request.SendFile(Request.URL());
}
请注意,直到处理程序完成后,实际内容才会被发送,因此 GuessMimeType
和 SendFile
函数的调用顺序无关紧要。Lacewing 中的所有函数都会立即返回,您可以进行设置 Cookie 和更改 HTTP 标头等操作,直到处理程序返回为止。
通过这个新的调用这两个函数的 GET
处理程序,现在可以将整个具有 HTML/CSS/JS 的网站放在 Web 服务器所在的同一个文件夹中,并从 Web 浏览器访问它。这可以与 C++ 中的自定义行为结合使用,以实现 C++ 驱动的 AJAX 应用程序(这是一个非常棒的想法)。目前,我只尝试使用了一张图片:
设置 HTTPS
您可能会惊讶地发现,Lacewing(在 Windows 上)不使用 OpenSSL 或任何其他库来实现 HTTPS。与库的其他部分一样,它基于操作系统提供的 API——Windows 提供了安全套接字函数,Internet Explorer 和 IIS 使用的就是这些函数。该 API 使用起来不太方便(如果您感兴趣,可以从 这里开始),但 Lacewing 将其很好地封装在 Lacewing::Webserver
中,提供以下函数:
HostSecure
- 类似于Host
函数,但用于 HTTPS 而非 HTTP。调用此函数后,将正常处理 HTTPS 请求的处理程序,但Request.Secure()
将返回true
。必须先调用LoadCertificateFile
或LoadSystemCertificate
。LoadCertificateFile
- 从证书文件(CER 或 PFX)加载证书。接受两个参数——第一个是证书文件的文件名,第二个是在创建证书时使用的通用名称。仅限 *nix 或 Windows Vista 及更高版本。LoadSystemCertificate
- 加载已导入到系统中的证书(通过打开证书文件并按照向导操作进行导入)。接受三个参数——第一个是存储名称(预定义的系统存储包括“MY”、“Root”、“Trust”和“CA”),第二个是在创建证书时使用的通用名称,第三个(可选)是查找的证书存储,默认为“CurrentUser”。有关系统证书存储的更多信息,请参阅 此处。仅限 Windows XP 及更高版本。
您需要从签名机构获取证书,或者生成一个自签名证书进行测试。这超出了本文的范围,但这里有一篇关于使用 OpenSSL 生成自签名证书的精彩教程:此处。
我已经将通用名称为“localhost”的测试证书导入,现在可以修改初始化代码如下:
Webserver.onGet(onGet);
Webserver.Host();
Webserver.LoadSystemCertificate("MY", "localhost");
Webserver.HostSecure();
现在,如果我访问 https:///lacewing_02.png...
表单
Lacewing::Webserver
完全支持两种请求参数:
- GET 参数是 URL 的一部分。如果您加载 https:///home?foo=bar,那么您可以在
GET
处理程序中使用Request.GET("foo")
,它将返回“bar
”。可以使用多个参数,形式为 https:///home?foo=bar&foo2=bar2。这些参数可以由用户手动添加,通过链接添加,或者通过method="GET"
的 HTML 表单添加。 - POST 参数是 HTTP 请求的正文,而不是 URL 的一部分。它们是通过
method="POST"
的 HTML 表单添加的,可以在POST
处理程序中使用Request.POST("name")
检索。
GET 参数受到 URL 长度的限制,GET 请求可能会受到浏览器缓存的影响。决定是使用 GET
还是 POST
的通用规则是:GET
用于指定读取数据的参数,而 POST
用于指定写入数据的参数。
GET 和 POST 参数都可以从 JavaScript 使用。我强烈推荐 jQuery,我已经在与 Lacewing::Webserver
作为后端的 Web 应用程序中广泛使用了它。Request
对象中的流 <<
操作符使得生成 JSON 变得非常容易,JSON 可以直接转换为 Javascript 对象。
我不会在本篇文章中涵盖这些内容,但可以在 GitHub 上的 库示例文件夹中找到一个示例。要尝试 POST
功能,您需要一个 POST
处理程序以及 GET
处理程序,就像下面这个简单的 HTML 表单测试一样:
void onGet(Lacewing::Webserver &Webserver, Lacewing::Webserver::Request &Request)
{
if(!strcmp(Request.URL(), "form"))
{
Request.SendFile("form.html");
return;
}
Request << "Page not found";
}
void onPost(Lacewing::Webserver &Webserver, Lacewing::Webserver::Request &Request)
{
if(!strcmp(Request.URL(), "submit"))
{
Request << "Hello, " << Request.POST("name") << "!";
return;
}
Request << "Page not found";
}
POST
处理程序可以与 GET
处理程序一起注册。
Webserver.onGet (onGet);
Webserver.onPost (onPost);
您还需要这个 form.html 文件:
<form action="submit" method="post">
Enter your name: <input name="name">
<input type="submit" value="Go!">
</form>
就是这样!如果您运行应用程序,应该可以访问 https:///form
并提交...
结论
虽然我没有涵盖 Lacewing::Webserver
的所有方面,但我希望本文能提供一个有用的介绍。其余函数都相当直观且命名一致,因此应该很容易上手。
如果您有任何疑问或建议,请随时发表评论或给我发送电子邮件。该库现在也在 GitHub 上开源,并设有问题跟踪器。
历史
- 2010 年 10 月 3 日:首次发布
- 2011 年 7 月 16 日:更新了新的开源 API