65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (30投票s)

2010年10月3日

CPOL

7分钟阅读

viewsIcon

127108

downloadIcon

4296

使用 Lacewing 网络库在 C++ 应用程序中嵌入 Web 服务器

引言

本文将介绍如何使用 Lacewing 网络库将嵌入式 Web 服务器集成到您的 Win32/C++ 应用程序中。它可以用于例如:

  • Web 管理 - 服务器应用程序通常有一个 Web 界面,用于远程管理 - 只需要一个 Web 浏览器即可管理服务器。
  • Web 应用程序 - 用 C++ 编写的 Web 应用程序可能比 PHP 或 ASP 更快、更具可伸缩性,资源占用更少,并且无需额外安装。

背景

我决定通过 Lacewing::Server 类进行实验,编写自己的 Web 服务器类。该类设计用于通过 IO 完成端口epollkqueue 进行扩展。

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.hLacewing.lib 的文件夹路径添加到 VC++ 目录中,包括头文件和库部分。在 Visual C++ 2008 中,这位于 工具 > 选项 > 项目和解决方案 > VC++ 目录下。添加路径后,您应该可以在代码中 #include <Lacewing.h>,这将自动链接 Lacewing.lib

使用该类

为了让 Lacewing 中的任何类正常工作,您首先需要一个 Lacewing::EventPump 实例,它负责监听事件并将其“泵送”给感兴趣的类。

然后,您可以创建一个 Lacewing::Webserver 实例,并将事件泵传递给构造函数。

Lacewing::EventPump EventPump;
Lacewing::Webserver Webserver (EventPump);  

这样就已经可以托管了,并且会显示一个空白页面。要使其显示一些有趣的内容,您需要注册一些处理程序。目前我们只关心响应 GET 请求,但还有更多可用的处理程序,用于 HEADPOST、错误处理和多部分文件上传等——请参阅 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,届时您将看到:

Welcome to 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());
}  

请注意,直到处理程序完成后,实际内容才会被发送,因此 GuessMimeTypeSendFile 函数的调用顺序无关紧要。Lacewing 中的所有函数都会立即返回,您可以进行设置 Cookie 和更改 HTTP 标头等操作,直到处理程序返回为止。

通过这个新的调用这两个函数的 GET 处理程序,现在可以将整个具有 HTML/CSS/JS 的网站放在 Web 服务器所在的同一个文件夹中,并从 Web 浏览器访问它。这可以与 C++ 中的自定义行为结合使用,以实现 C++ 驱动的 AJAX 应用程序(这是一个非常棒的想法)。目前,我只尝试使用了一张图片:

Lacewing logo transmitted over HTTP

设置 HTTPS

您可能会惊讶地发现,Lacewing(在 Windows 上)不使用 OpenSSL 或任何其他库来实现 HTTPS。与库的其他部分一样,它基于操作系统提供的 API——Windows 提供了安全套接字函数,Internet Explorer 和 IIS 使用的就是这些函数。该 API 使用起来不太方便(如果您感兴趣,可以从 这里开始),但 Lacewing 将其很好地封装在 Lacewing::Webserver 中,提供以下函数:

  • HostSecure - 类似于 Host 函数,但用于 HTTPS 而非 HTTP。调用此函数后,将正常处理 HTTPS 请求的处理程序,但 Request.Secure() 将返回 true。必须先调用 LoadCertificateFileLoadSystemCertificate
  • 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 logo transmitted over HTTPS

表单

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 logo transmitted over HTTPS

并提交...

Lacewing logo transmitted over HTTPS

结论

虽然我没有涵盖 Lacewing::Webserver 的所有方面,但我希望本文能提供一个有用的介绍。其余函数都相当直观且命名一致,因此应该很容易上手。

如果您有任何疑问或建议,请随时发表评论或给我发送电子邮件。该库现在也在 GitHub 上开源,并设有问题跟踪器。

历史

  • 2010 年 10 月 3 日:首次发布
  • 2011 年 7 月 16 日:更新了新的开源 API
© . All rights reserved.