使用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; } } }