使用Apache + knewcode,用传统C++构建Web网站
一、准备工作
1、编译器gcc
Linux(建议Ubuntu 14.04 32位版)下,最新版本需下载源代码自行编译,地址如下,
http://gcc.gnu.org/
Windows下,可以直接下载编译版本Mingw,下载地址如下,
http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/
Ubuntu下,如不需要最新版本,可用如下命令安装,
sudo apt-get install gcc g++
2、集成开发环境Code::Blocks
跨平台的开发环境,Windows下和Linux下都可使用,方便调试,下载地址如下,
http://www.codeblocks.org/downloads/26
Ubuntu下,可用如下命令安装,
sudo apt-get install codeblocks
3、网站平台Apache
下载地址,http://www.apache.org/
Ubuntu下,可用如下命令安装
sudo apt-get install apache2
4、支持C++的Apache模块knewcode
最新版本0.92a版,下载地址如下,
http://download.csdn.net/detail/zogyzen/9123653
(在debug目录下,附带了适用Windows的Apache2.2.25版)
非常适用于最低配置的阿里云或腾讯云,预装32位的Ubuntu 14.04版的服务器。
二、构建Web网站
1、hello world的例子
(1)、页面hello_world.kc
代码如下,
<!-- interface语句 --> <!--<% #interface<IHelloWorld>; #string HelloWorld(#string); %>--> <!-- load语句 --> <!--<% #load $mod = "/bin/hello_world_cpp"; #interface<IHelloWorld> CreateInf(); #void FreeInf(#interface); %>--> <!-- 定义 --> <!--<% #string $name = "tom"; #interface $in = $mod.CreateInf(); %>--> <html> <head> <title><% #print $name; %></title> </head> <body> <!-- 输出 --> <!--<% #print $in.HelloWorld($name); %>--> </body> </html> <!--<% $mod.FreeInf($in); %>-->
(2)、动态库hello_world_cpp
hello_world.cpp代码如下,需要编译为hello_world_cpp.dll(Windows下)或hello_world_cpp.so(Linux下); 放在网站根目录的“bin”子目录下,
#include <iostream> #include <string> using namespace std; #include "common_define.h" class IHelloWorld { public: virtual const char* CALL_TYPE HelloWorld(const char*) = 0; }; class CHelloWorld : public IHelloWorld { public: virtual const char* CALL_TYPE HelloWorld(const char* s) { m_s = string("Hello ") + s; cout << m_s << endl; return m_s.c_str(); } virtual ~CHelloWorld() = default; private: string m_s; }; extern "C" { IHelloWorld& CALL_TYPE CreateInf(void) { CHelloWorld* hw = new CHelloWorld; return *hw; } void CALL_TYPE FreeInf(IHelloWorld* inf) { delete dynamic_cast<CHelloWorld*>(inf); } }
2、knewcode简单介绍
C++一直被诟病为,不善于开发界面程序,不善于开发数据库程序,不善于开发Web程序等等。
微软的DotNet虽然包含了C++,但开发Asp.Net却不能使用C++(即便能使用,也是托管下的C++,不是标准C++),不过受DotNet启发,knewcode便孕育而生了。
knewcode与知名的PHP一样构建在Apachede平台上,借力于Apache的稳定与健壮等特性;不过,PHP是纯脚本的Web开发平台,而knewcode则实现了页面与业务的分离,页面部分使用传统的【HTML+JavaScript+CSS】加少量knewcode脚本实现,业务部分则是使用符合传统C++开发方式,编译实现。
(1)、knewcode的目录结构:
【debug】目录,附带了适用Windows的Apache2.2.25版,在Windows下将整个knewcode目录拷贝到D盘根目录下,执行“D:\knewcode0.92a\StartDebug.bat”,可启动Apache测试环境; 如果需要正式使用这个平台,请将Apache注册为Windows服务;如果需要更改所在目录,请打开Apache的conf\httpd.conf配置文件,修改相应配置的目录,“ServerRoot”、“DocumentRoot”、“LoadModule dlib_kc_module”、“dlib_kc_path”; 其它Apache的配置,请详见Apache的相关文档。
【hint_file】目录,存放提示信息的文件,如果一定要修改的话,请不要增加、修改、删除字符串中的标志位,如,%1%、%2%、%3%等。 【include】目录,开放使用的C++头文件,网站应用的C++动态库,需要引用“common_define.h”头文件,并将自定义的函数(全局或成员),定义为“CALL_TYPE”调用约定。
【lib】目录,扩展库目录,后续版本会有更多的扩展库,用户也可自定义扩展程序;
目前提供数据库扩展“db”,目前只支持sqlite和postgresql,后续版本会陆续加上Oracle、MySQL、DB2等;
Web Service扩展“service”,简单支持soap,尚不支持WSDL;
服务端会话状态扩展“session”;
网络扩展“net”和通用扩展“util”,功能还很简单,会逐步完善。
【modules】目录,knewcode主体功能的动态库文件,同时包含了Windows(.dll)和Linux(.so)的版本。
【sample】目录,三个例子,本目录被设置为Apache的网站根目录;
src\exam_blog,使用一个HTML5的模板,实现了一个简单的blog功能的例子,对应index.kc页面;
src\hello_world_cpp,简单网站业务层例子(对应hello_world.kc页面),和Web Service服务器端例子(对应webservice.kc页面);
src\webservice_client(c#),Web service客户端例子,使用C#编写。
【config.xml】配置文件
Parameters -> logging,日志类型,有debug、info、warning、error四个等级;
Parameters -> hint_file,提示信息的文件;对应【hint_file】目录中的文件名;
Directories -> lib,对应【lib】目录,“<lib>”标记可直接在页面脚本中使用,如“#include "<lib>/session/so_session.kc";”;也可添加自定义目录。
【kc_apache_mod.dll(或.so)】,Apache模块文件。
【kc_fastcgi_mod.exe】,fastcgi程序,可将knewcode配置在IIS7下使用。
(2)、基本脚本介绍
脚本括号:被【<!--<%或<%】和【%>-->或%>】括起来的字符串为knewcode的脚本;
注释:脚本中用大括号括起来的为注释;
#note和#end-note关键字:用于注释掉HTML文本,如,<!--<% #note %>-->文字<!--<% #end-note %>-->,中间的“文字”将不会在最终的页面上显示;
a)、定义语句,包括变量定义,加载动态库定义,接口定义,包含页面定义等:
#void、#int、#double、#string、#bool、#interface:数据类型关键字,#void只能用于定义无返回值的函数,其余类型可用于定义变量和函数形参;
#true、#false:bool类型的常量值;
函数定义:如,“#void ShowInfo(#int, #interface);”,首先,定义返回值类型,无返回值定义为“#void”,接下来是函数名,最后是用小括号括起来的形参列表;
#load:加载动态库的语句,如,“#load $mod1 = "/bin/hello_world_cpp";”,可加载“bin”目录下的“hello_world_cpp”动态库;不必写扩展名,Windows下默认扩展名为“.dll”,Linux下默认扩展名为“.so”;
定义加载动态库后,要马上定义该动态库内包含的函数列表,函数名要与动态库中的函数名对应;
一个脚本括号内只能定义一个加载动态库,不能再包含其他的语法元素;
#interface:定义接口的语句,如,“#interface<IExamBlog>;”,尖括号内为接口的名称;
定义接口后,要马上定义该接口包含的所有函数列表;该函数列表与C++接口定义中的虚函数对应,并且是按顺序对应(不是按函数名对应);
一个脚本括号内只能定义一个接口,不能再包含其他的语法元素;
#include:包含其它页面的语句,如,“#include "<lib>/session/so_session.kc";”,将把其它页面的内容包含到当前位置;
一个脚本括号内如果包含了#include,就只能包含一组#include,不能再包含其他的语法元素;
b)、表达式:
运算符:()、+、-、*、/、\、&&、||、!、^、==、!=、<、<=、>、>=
函数调用:如,“$in.HelloWorld($name);”,首先是接口变量名(或加载动态库名),接下来是成员提取符(实心点),最后是用小括号括起来的实参列表;返回值可以赋值给变量,也可以打印到页面上;
c)、结构语句,每个脚本括号内只能有一条语句,并且以分号结束语句。
#print:打印到页面语句;
#exit:退出页面语句;
#if、#else-if、#else、#end-if:条件分支语句;
#while、#end-while、#break、#continue:循环语句;
d)、内部变量:
$$get["参数名"]:链接串中get参数;
$$post["参数名"]:页面post参数;
$$cookie["名称"]:页面cookie值;
$$user["名称"]:自定义内部变量。
三、构建Web Service服务器端
1、服务器端的例子
(1)、业务部分同hello_world_cpp例子,见“一、1、(2)”;
(2)、页面webservice.kc
代码如下,
<!-- interface语句 --> <!--<% #interface<IHelloWorld>; #string HelloWorld(#string); %>--> <!-- load语句 --> <!--<% #load $mod = "/bin/hello_world_cpp"; #interface<IHelloWorld> CreateInf(); #void FreeInf(#interface); %>--> <!--<% #interface $iWebs = $mod.CreateInf(); %>--> <!--<% #include "<lib>/service/so_webservice.kc"; %>--> <!--<% $mod.FreeInf($iWebs); %>-->
2、客户端的例子(C#)
代码如下,
using System; using System.Configuration; using System.Collections.Generic; using System.Text; using System.Data; namespace webservice_client { // “iWebs”对应服务器端脚本的接口变量名 [System.Web.Services.WebServiceBindingAttribute(Name = "WebServiceBase", Namespace = "iWebs")] public class WebServiceBase : System.Web.Services.Protocols.SoapHttpClientProtocol { // 客户端cookie private static System.Net.CookieContainer g_cookies = new System.Net.CookieContainer(); // 构造 public WebServiceBase() { this.Url = ""http://127.0.0.1:8090/webservice.kc""; this.CookieContainer = g_cookies; this.Timeout = 3600 * 1000 * 8; } // 函数测试 [System.Web.Services.Protocols.SoapDocumentMethodAttribute(Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public string HelloWorld(string name) { return this.CallWebMethod<string>("HelloWorld", new object[] { name }, ""); } // 执行服务器端方法 protected T CallWebMethod<T>(string methodName, object[] parameters, T DefaultValue) { try { object[] result = this.Invoke(methodName, parameters); return (T)result[0]; } catch (Exception) { // throw; } return DefaultValue; } } }