一、 ISAPI 簡介
通用網關接口 CommonGatewayInterface(CGI) 很早就作為交互式的 Web 應用程序的一個標準廣泛應用在 Internet 之中。 CGI 腳本允許人們用多種編程語言 ( 如 Basic 、 C 、 Perl 、 Shell 等等 ) 來編寫簡單的應用程序。這些腳本運行在 Web 服務器上, 而在客戶的 Web 瀏覽器上輸出運行結果??蛻舻妮斎胪ㄟ^環境變量或者標準輸入設備來進行傳遞, 然后 CGI 程序根據需要完成特定的操作,并通過 HTML 格式顯示在客戶的瀏覽器中。 CGI 的這一特性給互聯網帶來了生機,網站的建設也從此從沉默的處子變為熱烈的少女,隨著時間的推移,這位曾經轟動一時的白雪公主也日益疲乏起來。
人們在長期的使用中還是發現了 CGI 應用程序的一個很大的缺點:性能不高。 我每請求一次 CGE 程序時, CGI 執行文件(或者腳本的解釋器)都要為每一個請求創建一個新的進程。對于一個信息量比較大的站點來說,這無疑給服務器增加了一個沉重的負擔。在以后的歲月里,熱鬧的互聯網又推出了 ASP , JSP 等好多提高安全性及服務器性能的措施,可其安全性及開發工具的成熟度遠遠不及 VC , Delphi 等成功的工具,而作為一個熟悉軟件編程的程序員來說,利用現成的開發工具及現有的資源開發一套網站系統更是得心應手。
ISAPI ( Internet Server Aplication Programing Interfase )就是一種用編程工具進行開發,、運行于 NT 服務的一種標準的編程接口, Web 開發者可用它編寫交互式應用程序。 ISAPI 擴展( Extension )應用程序具有 CGI 腳本同樣的功能,但它比 CGI 具有更快、更有效的性能。
ISAPI for Windows NT 編寫的應用程序, Web 用戶可通過填寫 HTML 表單( Form )或單擊 Web 節點上 HTML 頁面中的鏈接來激活該應用程序。服務器端獲取用戶提供的信息后,被激活的 ISAPI 應用程序對獲取的信息作出處理。根據 ISAPI 應用程序的功能或把獲取的信息存入數據庫,或以獲取的信息為條件訪問數據庫中的數據,然后把結果以 HTML 頁面的形式送回到客戶端。
ISAPI 的應用程序被編譯為動態鏈接庫( DLL, Dynamic Linked Library ),該庫在 WWW 服務( Service )啟動時裝載入內存。它要求較少的系統開銷,性能明顯優于 CGI 應用程序。因為每個請求并不啟動單獨的進程。而如用 CGI 作交互程序, 每個請求需要啟動單獨的進程。 ISAPI 有明顯的性能優勢,但其調試不方面,開發資料較少, 一直成為開發人員不優選的原因。為了更廣泛地運用 ISAPI ,特作此文,以供廣大開發者更快地進入開發過程。
二、一個簡單的 ISAPI 程序
New,Object,ISAPI Extension Wizard,Object Name 就起個 FirstISAPI,OK, 第二步什么也不要變,點 Finshed 完成。按下 F7 把生成的 DLL 放到 C:inetpubwwwrootfirstisapi 目錄下 ( 建文件夾不用我教吧? ), 打開瀏覽器,在地址欄內輸入
http://127.0.0.1/firstisapi/FirstISAPI.dll
OK, 我們立馬可以看到如下內容: This default message was produced by the Internet Server DLL Wizard. Edit your CFirstISAPIExtension::Default() implementation to change it.
你看到了嗎?
如果你看到了上面這一串英文字符,說明你的服務器可以用啦。
如果你看到了一個下載文件對話框,別哭!說明你的服務器還沒有配置好,下面由我來一步步教你,會用的朋友也別錯過這個好機會!
開始,程序,管理工具, Internet 服務管理器,打開左邊樹,找到 默認 Web 站點下的 firstisapi, 把鼠標入在上面單擊右鍵,屬性,在 執行許可 里面下拉列表,選中 腳本和可執行程序 其他什么也別動(如果你覺得動著好玩的話就動吧, 不過服務器配置不好了可別怪我沒提前 “ 聲明:) ” ) .
下面我們要做什么呢 ? 忘了 , 等等 ! 哦 , 對了 , 點擊 OK, 如果沒有呢 , 你的是中文版吧 , 那就點確定吧 !
好了 ,http://127.0.0.1/firstisapi/FirstISAPI.dll-->>>
This default message was produced by the Internet Server DLL Wizard. Edit
your CFirstISAPIExtension::Default() implementation to change it.
這回看到了吧 ?
還沒有 ?
算了 , 別學 ISAPI 了 , 多煩 , 你不煩我都煩了 , 臭機子 ! ( 想了想 , 這種可能還是有的 , 我還是為人為到底吧 , 免得你又罵娘了 , 好 , 把開光驅 , 把 2000 的盤放進去啊 , 還愣著干么 ? 添加組件 , 重裝一遍你的 INTERNET 服務 !......)
這回不會不行吧 ? 不行 ?
如此 , 我們的第一個 ISAPI 就做成了 , 你可以放心的說 , 我會做 ISAPI 了 ........---- 屁 ! 這也叫會 ?
對了 , 忘了 WIN98 的朋友 , 對不起啊 , 你有沒有裝 PWS 啊
沒有 ?
那裝一個吧 !
其他的和 2000 大同小異 , 不用我教吧 ?:):)
三、 ISAPI 實現 客戶 / 服務器 交互
我們現在已經可以做一個簡單的 ISAPI 程序了,但是,它什么功能也沒有實現, 畢竟我們還沒有加讓它動起來的代碼嘛!
在 CFirstISAPIExtension 類里象做一般程序一樣加入一個成員函數
void Getdata(CHttpServerContext *pCtxt, char *data);
然后在 BEGIN_PARSE_MAP(CFirstISAPIExtension, CHttpServer) 和
END_PARSE_MAP(CFirstISAPIExtension) 之間加入
ON_PARSE_COMMAND(Getdata, CFirstISAPIExtension,ITS_PSTR) 和
ON_PARSE_COMMAND_PARAMS("data") ,如下所示:
BEGIN_PARSE_MAP(CFirstISAPIExtension, CHttpServer)
// TODO: insert your ON_PARSE_COMMAND() and
// ON_PARSE_COMMAND_PARAMS() here to hook up your commands.
// For example:
ON_PARSE_COMMAND(Getdata, CFirstISAPIExtension,ITS_PSTR)
ON_PARSE_COMMAND_PARAMS("data")
ON_PARSE_COMMAND(Default, CFirstISAPIExtension, ITS_EMPTY)
DEFAULT_PARSE_COMMAND(Default, CFirstISAPIExtension)
END_PARSE_MAP(CFirstISAPIExtension)
ON_PARSE_COMMAND 和 ON_PARSE_COMMAND_PARAMS 是參數映射宏, ON_PARSE_COMMAND 的第
一個參數 Getdata 是要處理事件的函數名,第二個參數 CFirstISAPIExtension 為以 CHttpServer 為父類的應用程序子類,后面的參數要傳給 Getdata 函數的參數列表,它們可以是: ITS_EMPTY : 沒 有 數 據; ITS_PSTR : 字 符 串; ITS_I2 : short 整 型; ITS_I4 : long 整 型; ITS_R4 : float 浮 點 數; ITS_R8 : double 浮 點 數。 參數的個數一般與下面 ON_PARSE_COMMAND_PARAMS 解析的參數相對應。我們在這里可以 data 參數賦給一個初值,當我們在瀏覽器里沒有傳上來這個參數的實際數值時就是它了。如 data=www.ourcode.net; 這里 data 就是我們定義函數 Getdata 里所要傳入的參數名。
有了上面這些準備工作,下面我們就可以添加代碼了,因為它和平常編程時沒什么兩樣,因此我們就簡單地加上一點代碼看看我們勞動了半天到底做了些什么?
如下:
void CFirstISAPIExtension::Getdata(CHttpServerContext *pCtxt, char *data)
{
StartContent(pCtxt);// 寫 HTML 文件頭
WriteTitle(pCtxt);// 寫標題
*pCtxt << _T(" 這是你提交上來的數據: rn");// 向瀏覽器發送數據
*pCtxt <<data <<"rn";// 寫上一個換行和回車
EndContent(pCtxt)
}
CHttpServerContext *pCtxt 是一個服務器上下文標志, 我們向瀏覽器傳輸數據就全靠
它。 *pCtxt << 后的字串就是瀏覽器里的 HTML 源碼,因此,你可以把 " 這是你提交上來的數據:
rn" ,改成你想要的 HTML ,想怎么改隨你吧?。海?/span>
現在是不是要按下 F7 了?哈哈,好, F7 ,馬上把 FirstISAPI.dll Ctrl+C,Ctrl+V........
" 無法創建或替換 FirstISAPI: 指定的文件正被 Windows 使用 " ,沒法了?我就重起計算機啦,誰教它裝入內存呢?。ㄆ鋵嵵灰貑?/span> IIS 或結束進程 Win2003 下 w3wp.exe , Win2000 下 aspnet_wp.exe )
下面 ISAPI 調試的文章,不愛老重起計算機的朋友可以看一下。
ISAPI 在運行時是 IIS 的一部分,而 IIS 又作為 NT 的一個服務而運行。這一事實使用調試過程變得復雜了,因為在 IIS 運行時, VC++ 的調試器不能夠接管 ISA 。為了解決這個問 題,微軟公司以兩種形式發行了 IIS :作為一項服務,以及作為一個單獨的可執行程序。 對于后一種情況,我們就可以在命令行上來控制服務器。雖然這樣可以解決上述問題并使得開發過程變得容易一些,但實現起來顯得很繁瑣。下面我們來介紹這個過程。
當用戶處于 debug 調試模式時, VC++( 以及 IIS) 將在用戶的帳號和權限下運行。由于通常 IIS 完成的一些工作是不允許大多數用戶有相應的權限的,因此用戶(或用戶的系統管理員)需要做以下工作:
在桌面上選擇 “ 開始程序管 理工具(公用)域用戶管理器 ”, 打開域用戶管理器;
在 “ 規則 ” 菜單中選擇 “ 用戶 權限 ” ;
選擇 “ 顯示高級用戶權限 ” 檢 查框;
在 “ 權限 ” 下拉列表中選擇 “ 以操作系統方式操作 ” ;
選擇 “ 添加 ” 按鈕得到 “ 添加 用戶及組 ” 對話框,選擇 “ 顯示用戶 ” 按鈕,并在 “ 名 稱 ” 列表中選擇用戶使用的帳號,然后選擇 “ 添加 ” 按鈕;
選擇 “ 確定 ” 按鈕;
對 “ 產生安全審核 ” 權限重復 上述步驟。
為了使這些設置生效,用戶必 須先退出登錄,然后再登錄回來。
IIS 中包含了三項服務: FTPPublishing Service,GopherPublishingService 和 WorldWideWeb 。由于調試器要在命令行上運行 IIS ,所以所有這三項服務都必須停止。 這可以通過 “ 控制面板 ” 中的 “ 服務 ” 程序或者使用 IIS 的 “Internet 服務管理器 ” 來實現。如果需要進行大量的調試工作, 我們建議用戶通過 “ 控制面板 ” 中的 “ 服務 ” 程序來關閉 IIS 服務并禁止它們自動啟動,這樣可以避免用戶每次啟動計算機時都要進行關閉服務的操作。
接下來就必須對工程進行一些 配置了:
在 Project 菜單中選擇 Settings 菜單 項;
選擇 Debug 面板,并在 Category 下拉 列表中選擇 General ;
在 Executablefordebugsession 框中輸入 或者尋找 IIS 執行文件的路徑 (通常情況下位于 WINNTsystem32inetsrvinetinfo.exe );
在 Programarguments 框中輸入 -ew3svc ,如 圖 3 所示;圖3 Debug 面板設置選擇 Link 面板;
在 Outputfilename 框中輸入被編譯 后的 DLL 將被放置的路徑和文件名。這個路徑必須位于 Web 服務器的根目錄下或者某個虛擬目標下,以便客戶可 以通過 URL 來訪問。例如,我們的 Web 服務器的目錄是 c:InetPubwwwrootfirstisapi ,我們把 firstisapi.dll 放置在該目錄下,這樣客戶就可以使用 下面的 URL 來訪問它:
http://127.0.0.1/firstisapi/firstisapi.dll
如果用戶現在還沒有退出登錄 以改變權限,請現在行動,然后再登錄回來。
如此,我們的服務器 DLL 就大功告成啦,趕快在瀏覽器里輸入 http://127.0.0.1/firstisapi/firstisapi.dll?Getdata&data= 這是我發給服務器的信息!
OK ?
這是你提交上來的數據:
這是我發給服務器的信息!
OK !
要是我們想以 FORM 的表單形式向服務器提交數據呢?
把下面這段 HTML 源碼存為一個 firstisapi.htm 文件,放在 DLL 相同目錄下。
<form method="post" action="firstisapi.dll?">
<p align="center"> </p>
<input type="hidden" name="MfcISAPICommand" VALUE="Getdata">
<p align="center"> 要提交的數據: <input type="text" name="data" size="20"></p>
<p align="center"><input type="submit" value=" 提交 " > </p></form> </body>
四、 ISAPI 操作數據庫
上面我們定義了一個 Getdata 函數 , 在函數里我們實現了這樣一個功能 : 把客戶傳送上的數據發給客戶 . 要是操作數據庫呢 ? 一樣的道理 , 我們只要在自己定義的函數里添加上操作數據庫的代碼 , 就可以實現存取數據庫 . 輸入的數據在變量 data 里,輸出給客戶機用 *pCtxt 發給瀏覽器。
現在我們建立一個數據庫,用什么庫由你選取吧,可以是 ACCESS,SQL Server ,Sbase 等,我們只要用數據源來操作它就行了,我們在建好庫后建立系統數據源(不要建成用戶數據源???為什么?問比爾 . 蓋茨去吧) FirstODBC, 用下面的 SQL 建立一個簡單的表。
表 : 用戶資料
create table user_info (
id int (4) not null,
friend_id int(4) not null,
user_name varchar (30) not null,
user_sex varchar (2) not null,
address varchar (20),
age int (4),
zip char (6),
phone varchar (20) )
有了數據源,我們就可以添加代碼實現數據庫的查詢,修改等。同上一節一樣,在 CFirstISAPIExtension 里添加一個成員函數
void Reg(CHttpServerContext *pCtxt,char *user_name,char *sex,char*address,int age,
char*zip,char*phone)
參數分別是 : 用戶名 , 性別 , 地址 , 年齡 , 郵編和電話 . 我們要它實現的功能是把用戶通過表單提交的數據寫到 FirstODBC 數據庫 . 然后同樣在
BEGIN_PARSE_MAP(CFirstISAPIExtension, CHttpServer) 和
END_PARSE_MAP(CFirstISAPIExtension) 之間加入
ON_PARSE_COMMAND(Reg, CFirstISAPIExtension,
ITS_PSTR ITS_PSTR ITS_PSTR ITS_I2 ITS_PSTR ITS_PSTR)
和 ON_PARSE_COMMAND_PARAMS("user_name sex address='' age=20 zip='' phone='')
,如下所示:
BEGIN_PARSE_MAP(CFirstISAPIExtension, CHttpServer)
// TODO: insert your ON_PARSE_COMMAND() and
// ON_PARSE_COMMAND_PARAMS() here to hook up your commands.
// For example:
ON_PARSE_COMMAND(Reg, CFirstISAPIExtension,ITS_PSTR ITS_PSTR ITS_PSTR ITS_I2 I
ITS_PSTR ITS_PSTR)
ON_PARSE_COMMAND_PARAMS("user_name sex address age zip phone)
ON_PARSE_COMMAND(Getdata, CFirstISAPIExtension,ITS_PSTR)
ON_PARSE_COMMAND_PARAMS("data")
ON_PARSE_COMMAND(Default, CFirstISAPIExtension, ITS_EMPTY)
DEFAULT_PARSE_COMMAND(Default, CFirstISAPIExtension)
END_PARSE_MAP(CFirstISAPIExtension)
下面我們定義自己的函數并進行數據庫的操作 , 單擊左邊 FirstISAPI classes,New Class,
Name:CUserSet,Base class:CRecordset---------------OK,ODBC:FirstODBC,
Table Name:user_info,OK.
( 請先看看 stdafx.h 里有沒有 #include )
void Reg(CHttpServerContext *pCtxt,char *user_name,char *sex,char*address,
int age,char*zip,char*phone)
{// 打開數據庫源代碼
StartContent(pCtxt); // 打印 <HTML> 和 <BODY> 標記
WriteTitle(pCtxt); // 打印 <TITLE> 、 “ 標題內容 ” 和 </TITLE> 標記等
CDatabase db;
if(!db.Open(_T("FirstODBC"), // (系統源名)
FALSE, //bExclusive
FALSE, //bReadOnly
_T("ODBC; UID=sa; PWD=;"), //lpszConnect (與數據庫的連接方式)
FALSE))
{ //bUseCursorLib
*pCtxt << "Could not open database.";
return;
}
CRecordset set(&db);
try
{
set.Open();
}
catch (CDBException* pEx)
{
*pCtxt << _T("Error Selecting from table:");
TCHAR szErrorMessage[1024];
if (pEx->GetErrorMessage(szErrorMessage, sizeof(szErrorMessage)))
{
*pCtxt << szErrorMessage;
*pCtxt << _T("rn");
}
return;
}
set.AddNew();
set.Edit();
set.m_id=0;
set.m_user_name=user_name;
set.m_sex=sex;
set.m_address=address;
set.m_age=age;
set.m_zip=zip;
set.m_phone=phone;
set.Update();
set.Close();
db.Close();
*pCtxt << _T(" 你已成功注冊 !rn");
// 打印 </BODY> 和 </HTML> 標記
EndContent(pCtxt);
}
把下面的 HTML 存為 reg.htm
<HTML>
<BODY>
<CENTER>
<FONT COLOR='#FF0000'><H3> 請在下面輸入你的注冊信息 ( 含 * 的為必填內容 )</H3>
</FONT>
</CENTER>
<HR>
<CENTER>
<form method="post" action="firstisapi.dll?">
<p align="center"> </p>
<input type="hidden" name="MfcISAPICommand" VALUE="Reg">
<p align="center"> 姓名: <input type="text" name="user_name" size="20"></p>
<p align="center"> 性別:
<select name="sex" size="1">
<option selected value="1"> 男 </option>
<option value="0"> 女 </option>
</select></p>
<p align="center"> 地址: <input type="text" name="address" size="20"></p>
<p align="center"> 年齡: <input type="text" name="age" size="20"></p>
<p align="center"> 郵編: <input type="text" name="zip" size="20"></p>
<p align="center"> 電話: <input type="text" name="phone" size="20"></p>
<p align="center"><input type="submit" value=" 提交 " > </p></form>
</center>
</BODY>
</HTML>
然后放到 firstisapi 目錄里 ,..................
http://127.0.0.1/firstisapi/reg.htm 這樣在瀏覽器的表單里輸入你的數據后提交 , 看看你我服務器數據庫 , 哈 , 有了 -----------> 你已成功注冊 !! 本想再做一個查詢程序 , 可是時間太緊 , 還是自己想想吧 , 和注冊程序沒什么兩樣 , 只要再加一個 Chaxun(...) 就 OK That is all right! 程序還有一些不完美的地方就是還不能檢驗用戶名是否已被注冊,還沒有到得 ID 的最大值,不過在此只是為了演示如何操作數據庫,筆者因此沒有深慮,有興趣的朋友自己可以加上這方面的代碼。
五、利用 ISAPI 進行網絡數據傳送
所謂網絡數據傳送 , 簡單地說 , 就是把數據從一臺計算機傳輸到網絡上的另外一臺計算機 . 說起數據傳輸 , 大家最熟的不過 socket 套接字了 , 無論在 UNIX,WINDOWS, 它一貫都是公認的傳輸方案 . 筆者在研究了 ISAPI 之后 , 成功地實現了用 ISAPI 進行網絡數據傳輸 , 并且解決了局域網防火墻內數據的有效傳輸 .
首先 , 我們要建立服務器數據接收 ISAPI 程序 . 在第三節 <ISAPI 實現 客戶 / 服務器 交互 > 里我們已經建立了一個 Getdata(..char *data) 函數 , 其中 data 里存放的就是我們從客戶機傳上來的數據 , 在這個函數里 , 我們就可以對它進行處理 , 如寫文件 , 寫數據庫等 , 再通過 *pCtxt 向客戶發送回執 . 關于如何寫庫寫文件已超出本節范圍 , 在此不再贅述。
在第三節里,我們是通過 FORM 表單通過瀏覽器把數據發送給服務器,在這里,我們要通過我們客戶機的程序向服務器發送數據。在你的程序里添加一個成員函數:
BOOL SendData(char *ip,char *request, char *buffer, unsigned int bufferlen) ,
其中參數 ip 為服務器 IP 地址,如果是本機調,傳入 127.0.0.1;request 為要發送到服務器的數據,最終由 Getdata ()接收,存放在 data 里供服務器處理程序處理 ;buffer 為服務器傳回回執存放綬沖區,即 *pCtxt 串行回的數據 bufferlen 為 buffer 綬沖區長度。 SendData 代碼如下:
BOOL SendData(char *ip,char *request, char *buffer, unsigned int bufferlen)
{
int ret=0;
CString URL;
URL="http://";
URL+=(CString)ip;
URL+="/firstisapi/firstisapi.dll?Getdata&data=";
URL+=(CString)request;
//
//AfxMessageBox(URL);
CInternetSession session;
CInternetFile *cf;
//----------------
try
{
cf = (CInternetFile * )session.OpenURL (URL);
}
catch (CInternetException * Exp)
{
char err[1024];
Exp->GetErrorMessage (err, 1024, NULL);
AfxMessageBox(err);
cf = NULL;
Exp->Delete ();
}
if (cf)
{
ret=cf->Read(buffer, bufferlen);
cf->Close ();
}
session.Close ();
return(ret);
}
這樣我們在程序里可以這樣調用它:
void CSassDlg::OnButton1()
{
// TODO: Add your control notification handler code here
CString ip="127.0.0.1",data=" 這是我要發給服務器的數據 ",buffer;
BOOL b=SendData((char *)LPCTSTR(ip),(char *)LPCTSTR(data),
buffer.GetBuffer(1204), 1024);
if(b)
{
MessageBox(" 數據發送成功 ");
}
}
這樣,我們只要把要發送給服務器的數據傳給 SendData 的第二個參數,服務器 data 就可收到,你想怎么處理就怎么處理吧。
六、 ISAPI 傳輸數據的加密
為了提高數據傳的安全性,可靠性,我們在數據傳輸之前都要對數據進行加密,但是無論哪種加密算法,進行加密的數據都有可能出現諸如 "@#$%^&*-+" 及不可字符,可是用 ISAPI 這種方法傳輸時, 服務器經過 ON_PARSE_COMMAND 和 ON_PARSE_COMMAND_PARAMS 參數映射宏進行處理后,如 "%,&" 等字符將會進行自動轉換或作為分隔字符處理 , 這樣, 服務器收到的數據將會是錯誤的。為了保證數據傳輸的可靠性,筆者經過多方面研究,自己定義了下面兩個函數,分別作為客戶機加密后上傳前數據處理和服務器接收到數據后解密前處理??蛻魴C加密后上傳前數據處理函數:
void code(char *buf, int len, char *outstr)// 加密后處理
{
CString str;
int outstr_len;
outstr_len=0;
char *tmp;
tmp=new char[16];
outstr[0]=0;
for (int i=0; i<len ;i++)
{
tmp[0]=0;
if(buf[i]=='%'||buf[i]=='&'||
buf[i]=='!'||buf[i]=='?'||
buf[i]=='+'||buf[i]==''||
buf[i]=='/')
{
str.Format("%%%x",buf[i]);
strcpy(tmp,str);
outstr_len+=3;
}
else
{
tmp[0]=buf[i];
tmp[1]=0;
outstr_len++;
}
if (buf[i]=='$')
{
strcpy(tmp,"$$");outstr_len+=2;}
if (buf[i]==0)
{
strcpy(tmp,"$0");outstr_len+=2;
}
strcat(outstr,tmp);
}
return;
}
其中 buf 為傳入數據綬沖區指針, len 為 buf 數據長度, outbuf 為處理后數據存放綬沖區 . 處理時將諸如 "%,&,+" 等特殊字符及 0 字符進行處理,這樣, 數據傳送到服務器后將會是正確的,之后經過服務器處理函數進行還原,再解密,那我們的數據就不會有錯了。
服務器接收到數據后解密前處理函數:
int decode(char *buff, int len, char *outstr)
// 上傳后處理
{
int i, p;
char v;
p=0;
for (i=0; i<len; i++)
{
if (buff[i]=='$')
{
if (buff[i+1]=='$')
{
v='$';
}
else
{
if(buff[i+1]=='0')
v=0;
}
i++;
}
else
{
v = buff[i];
}
outstr[p]=v;
p++;
}
return(p);
}
其中 buff 為傳入數據綬沖區指針, len 為 buff 數據長度, outstr 為處理后數據存放綬沖區 .
處理流程:對 Data 進行加密 -->code 函數進行處理 --> 服務器接到 data-- >decode 進行上傳后處理 --> 解密
七、 ISAPI 網絡數據傳送進階(網絡大數據包傳送)
筆者曾嘗試用上述方法傳輸大數據包,可是發現這種方法只可傳輸小于 2K 的數據包,這無疑成為它一個缺點。經過多方面的研究,發現這種方法是用 GET 方法發送數據, 操作系統本身就限制了它的數據量。以后經過測試,如用 POST 方法傳輸,發送數據包的大小將不受限制。下節將講解如何用 POST 方法向服務器發送數據。
BOOL PostData(char *ip,char *request,char *out_buf,int buf_len)
{
BOOL ret=FALSE;
char *buffer,*buff,*out;
buffer=new char[2048];
int bb_len=strlen(request);
buff=new char[bb_len];
out=new char[bb_len+1000];
CInternetSession session;
CHttpFile* pFile=NULL;
CHttpConnection* pConnection=NULL;
CString m_ip,sql;
m_ip=(CString)ip;
//---------------------------- 對 request 加密
strcpy(buff,request);
for(int i=0;i<bb_len;i++)//
buff[i]=buff[i]^(char)((i+0x56)%255);
code (buff,bb_len,out);// 上傳前處理
//-----------------------------------
sql=(CString)out;
CString strHeaders =_T("Content-Type: application/x-www-form-urlencoded");
CString str,strFormData = _T("data=");
strFormData+=sql;
try
{
pConnection =
session.GetHttpConnection(m_ip);
pFile =
pConnection->OpenRequest(CHttpConnection::HTTP_VERB_POST,
_T("/firstisapi/firstisapi.dll?Getdata"));
BOOL result = pFile->SendRequest(strHeaders,
(LPVOID)(LPCTSTR)strFormData, strFormData.GetLength());
DWORD dwRet;
pFile->QueryInfoStatusCode(dwRet);
//--------------
if (dwRet == HTTP_STATUS_OK)
{
buf_len=pFile->Read(buffer,2048);
str=(CString)buffer;
str=str.Left(buf_len);
ret=TRUE;
}
else
str=" 服務器處理錯誤! ";
pFile->Close ();
delete pFile;
pConnection->Close();
delete pConnection;
}
catch(CInternetException* pEx)
{
TCHAR szErr[1024];
pEx->GetErrorMessage(szErr, 1024);
str=szErr;
pEx->Delete();
}
catch (...)
{
str=" 和服務器通信時發生意外! ";
}
session.Close();
strcpy(out_buf,str);
buf_len=str.GetLength ();
MessageBox(NULL, str, " 系統提示 ", MB_OK);
delete buffer;
delete buff;
delete out;
return ret;
}
其中參數 ip 為服務器 IP 地址,如果是本機調,傳入 127.0.0.1; request 為要發送到服務器的數據,最終由 Getdata ()接收,存放在 data 里供服務器處理程序處理 ;buffer 為服務器傳回回執存放綬沖區,即 *pCtxt 串行回的數據 bufferlen 為 buffer 綬沖區長度。服務器接收程序同前,這樣,我們就可以發送大于 2K 的數據了。