一个简单的 C++ 嵌入 Web 服务器
引言
你有一两个网页吧?不一定是多么神奇的东西,但一个通过几个HTML标签作出的简洁的演示就可以。你有一个需要远程控制的复杂的C++ Windows 桌面应用程序吧?所以,不需要学习一个全新的技术,让我们一起为您的应用添加WEB页面吧。
Webem是一个可以嵌入你的C++应用程序的WEB服务器。它可以轻松地实现一个从任何地方都能访问的浏览器GUI。
Webem基于一个简化版的boost::asio WEB服务器,它可以让HTML代码执行C++方法。尽管你不需要查看服务器代码来使用Webem,但你需要为你的工程下载和使用BOOST库。我建议如果你从未使用过BOOST,那Webem可能不适合你。
源码下载 - 751.1 KB
------------------------------------------分割线------------------------------------------
具体下载目录在 /2014年资料/10月/26日/一个简单的 C++ 嵌入 Web 服务器
------------------------------------------分割线------------------------------------------
将C语言梳理一下,分布在以下10个章节中:
- Linux-C成长之路(一):Linux下C编程概要 http://www.linuxidc.com/Linux/2014-05/101242.htm
- Linux-C成长之路(二):基本数据类型 http://www.linuxidc.com/Linux/2014-05/101242p2.htm
- Linux-C成长之路(三):基本IO函数操作 http://www.linuxidc.com/Linux/2014-05/101242p3.htm
- Linux-C成长之路(四):运算符 http://www.linuxidc.com/Linux/2014-05/101242p4.htm
- Linux-C成长之路(五):控制流 http://www.linuxidc.com/Linux/2014-05/101242p5.htm
- Linux-C成长之路(六):函数要义 http://www.linuxidc.com/Linux/2014-05/101242p6.htm
- Linux-C成长之路(七):数组与指针 http://www.linuxidc.com/Linux/2014-05/101242p7.htm
- Linux-C成长之路(八):存储类,动态内存 http://www.linuxidc.com/Linux/2014-05/101242p8.htm
- Linux-C成长之路(九):复合数据类型 http://www.linuxidc.com/Linux/2014-05/101242p9.htm
- Linux-C成长之路(十):其他高级议题
背景
现有的嵌入式C++ web服务器使用起来是一个挑战,并且有不对Windows友好的趋势. 它们也不是你想要用来为你的实验性应用程序加入可以用手机进行监控的能力的那种东西.
我尝试过 (http://www.webtoolkit.eu/wt) ,但在安装和学习中挫败了.
最近我开始使用John Bartas的Webio. 我喜爱其理念,它也运作的很好.
然而,我仍然发现它在使用时过于复杂,并且服务端代码也难于理解. 我想要的是一个容易使用,基于一个知名web服务器,只做了轻微修改的好东东.
Webio的许多复杂性是有使用一个HTML编译器来隐藏控制着嵌入于应用程序代码里面的文件系统的外观的HTML页面所造成的. 我更喜欢将HTML页面放在外部一个通常的视图中,那样我就可以不用重新编译程序,却可以调整GUI.
我在尝试调整Webio以符合自己喜好的过程中了解了很多, 最终决定准备去构建一个能切实满足我自己的需求的东西.
代码使用
你可以像建立网站一样来建立的你应用程序GUI-从index.html开始使用HTML新建页面。
现在你需要使HTML调用你的C++方法。你需要做以下三件事:
创建被包含在WEB页面里的能生成HTML的include(包含)方法。
创建当用户点击WEB页面里的按钮时被webem调用的action(动作)方法。它们可以是简单的按钮,或html表单。
创建"web控件",即上面两个的组合,一个include方法生成表单,当用户点击按钮时这个表单会调用action方法-示例程序"Calendar"展示了如何创建一个显示和更新数据表的控件。
"Hello,world!"
第一步:新建web页面。你可以随你喜好将页面设计的很精心,但是对于我们的第一个“hello,world”应用,页面当然是越简单越好:
The Webem Embedded Web server says: <!--#webem hello -->
在尖括号中的文本告诉webem在应用中何处将文本包含进来,“hello”则是指定的应用方法,必需被调用来提供包含的文本。
第二步:新建类,该类返回“hello”:
/// An application class which says hello
class cHello
{
public:
char * DisplayHTML()
{
return "Hello World";
}
};
第三步:初始化wenem,配置端口和地址来监听浏览器请求并且去找到index.html作为web页面的首页。
// Initialize web server.
http::server::cWebem theServer(
"0.0.0.0", // address
"1570", // port
".\\"); // document root
第四步:用webem注册应用方法:
cHello hello;
// register application method
// Whenever server sees <!--#webem hello -->
// call cHello::DisplayHTML() and include the HTML returned
theServer.RegisterIncludeCode( "hello",
boost::bind(
&cHello::DisplayHTML, // member function
&hello ) ); // instance of class
第五步:最后,你已经准备好了启动服务。
// run the server
theServer.Run();
一个正式的Hello
让我们来创建一个更儒雅的程序,可通过姓名(如CodeProject,Canadian)来定位网络。
第一步:新建站点:
What is your name, please?
<form action=name.webem>
<input name=yourname /><input value="Enter" type=submit />
</form>
The Webem Embedded Web server says: <!--#webem hello -->
该表单提供一个文本域,可以使用户输入姓名。另外还有一个提交按钮将姓名提交给服务器。表单属性“action=name.webem”确保webem服务器会调用应用通过“name”注册的方法来处理输入。
第二步:创建应用类:
/// An application class which says hello to the identified user
class cHelloForm
{
string UserName;
http::server::cWebem& myWebem;
public:
cHelloForm( http::server::cWebem& webem ) :
myWebem( webem )
{
myWebem.RegisterIncludeCode( "hello",
boost::bind(
&cHelloForm::DisplayHTML, // member function
this ) ); // instance of class
myWebem.RegisterActionCode( "name",
boost::bind(
&cHelloForm::Action, // member function
this ) ); // instance of class
}
char * DisplayHTML()
{
static char buf[1000];
if( UserName.length() )
sprintf_s( buf, 999,
"Hello, %s", UserName.c_str() );
else
buf[0] = '\0';
return buf;
}
char * Action()
{
UserName = myWebem.FindValue("yourname");
return "/index.html";
}
};
这个类存储了对webem服务器的引用. 它允许在其构建时维护对其自身方法的注册, 并调用cWebem类的FindValue()来提取输入表单域中的值.
这个类需要注册两个方法,一个在表单提交时保存输入的用户名,一个用来在页面被组合起来发送给浏览器时展示被存储的用户名称.
动作方法必须返回要在提交按钮点击响应中展示的web页面.
注意所有的动作方法都是有Webem在包含方法之前调用的,所以web页面总是会展示更新了的数据.
第三步:构建webem,构建应用类并启动服务器:
// Initialize web server
http::server::cWebem theServer(
"0.0.0.0", // address
"1570", // port
".\\"); // document root
// Initialize application code
cHelloForm hello( theServer );
// run the server
theServer.Run();
你可能需要在其他线程中启动服务器,这样你应用能够继续在实验室装置中记录日志数据。为了做到这一点,更改对server::run的调用:
boost::thread* pThread = new boost::thread(
boost::bind(
&http::server::server::run, // member function
&theServer ) ); // instance of class
Webem 控件
Webem控件是以标准方式监视显示和操作��用数据的细节的类,所以应用程序开发者不需要关注生成HTML文本的所有细节。
示例程序使用一个webem来列出一个SQLITE数据表的所有内容,且提供了添加和删除记录的能力。在本文的开始是它的截图。