程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 用C++Builder開發ISAPI擴展應用程序

用C++Builder開發ISAPI擴展應用程序

編輯:關於C++

一、ISAPI概述 Microsoft的WEB服務器提供了不同的ISAPI,應用ISAPI能夠開發出高性能的應用程序。

ISAPI具有兩類組件:ISAPI擴展和ISAPI過濾器,本文著重介紹ISAPI擴展的應用和開發。

ISAPI應用程序通過DLL實現,DLL的特性使它能夠作為WEB服務器自身的擴充來裝載。在WEB服務器的地址空間運行, 而且只在第一次請求時裝載一次,以後每一個後續請求通過創建一個線程(僅用一個簡單的函數調用) 來完成,這比CGI創建一個進程要節約大量 的時間和空間等資源。

ISAPI擴展通常代替傳統Web應用程序中CGI腳本的位置,由客戶觸發,為其特殊請求服務。

------清單1--------------

1.<html>

2. <img src="myGetGrp.dll">

3.</html>

如清單1所示代碼,服務器將調用myGetGrp.dll中提供的函數得到一GIF圖像文件數據發 送給客戶浏覽器,在這裡,myGetGrp.dll就是一個ISAPI擴展。

如果服務器確定將執行一個ISAPI擴展, 他首先檢查此擴展是否已經裝入高速緩存,若沒有,則指定的DLL被裝載;裝入DLL後,服務器就調用DLL中的HttpExtensionProc()函 數對請求提供服務, 這裡是ISAPI程序員放置具體功能操作的位置,服務器將所有必要 的信息通過一結構類型參數傳遞給這個函數,包括請求本身的內容和程序員將用到的回 調函數等,用回調函數,可以將數據傳遞給用戶以及執行其他的操作。

注意:必須牢記服務器是啟動多線程來處理同時接收到的多個請求的,所以必須正確處理線程間的同步,否則將會導致數據破壞甚至系統崩潰。

二、用C++ Builder開發ISAPI擴展應用程序 C++ Builder是Inprise公司繼Delphi之後開發的又一個通用的客戶/服務器結構的 開發工具。 它使用了C++語言,可以產生更快速和更高效的代碼。目前已成為繼Visual Basic、Delphi之後,在32位Windows環境下最具有吸引力的開發工具之一。

啟動C++ Builder後,用File→New菜單項打開New Items對話框,在New頁面下選中 Web Server Application選項,單擊<OK>按紐,彈出一New Web Server Application對 話框,選中ISAPI/NSAPI Dynamic將生成一ISAPI擴展應用程序框架,其主模塊缺省名為 Project.cpp,其中主要實現了DLL的三個輸出函數,說明如下:

1.1 GetExtensionVersion()函數 這是一個非常簡單的函數,它唯一的目的是指定ISAPI版本,並給出擴展的描述。 當DLL第一次被裝載時,由服務器調用。這發生在HttpExtensionProc()函數第一次調用 前, 在此函數中, 你所需做的全部就是用常量HSE_VERSION_MAJOR和HSE_VERSION_ MINOR(在Isapi2.hpp中定義)設置擴展版本域。並且返回一true值。

GetExtensionVersion()函數實現實例見清單2。

---------清單2-----------------

BOOL_export WINAPI GetextensionVersion(Isapi2::THSE_VERSION_INFO &Ver)
   { /*設置擴展版本域*/
   Ver.dwExtensionVersion=MAKELONG(HSE_VERSION_MAJOR,HSE_VERSION_MINOR);
   Ver.lpszExtensionDesc="Example ISAPI extension";//設置擴展描述域
   return true; //返回true
   }

在此函數中,程序員還可以加入初始化代碼,如全局變量的初始化等。

1.2 HttpExtensionProc()函數

HttpExtensionProc() 函數是擴展的功能實現部分,每次產生對擴展的請求,服務器就調用這個函數,同時傳遞一類型為TEXTENSION_CONTROL_BLOCK結構的參數(ECB) , 這個結構在Isapi2.hpp中定義:

struct TEXTENSION_CONTROL_BLOCK
   {
   unsigned cbSize;   //結構大小
   unsigned dwVersion;  //版本信息
   unsigned ConnID;   //正在被服務的連接的ID;調用回調函數時必須作為一參數傳遞
   unsigned dwHttpStatusCode; //在HttpExtensionProc () 函數返回前,在此放置HTTP狀態碼,
                 //參見HTTP1.0規范定義
   char lpszLogData[80];  //接收一記錄信息字符串
   char *lpszMethod;  //命名請求方式的字符串的指針
   char *lpszQueryString;  //含一個GET請求查詢的字符串指針
   char *lpszPathInfo;  //請求的字符串的指針
   char *lpszPathTranslated; //把請求的字符串指針翻譯為服務器的物理路徑
   unsigned cbTotalBytes;  //請求中字節的全部數目
   unsigned cbAvailable; //lpbData緩沖區長度
   void *lpbData;   //POST請求的數據緩沖區指針
   char *lpszContentType;  //識別請求的MIME內容類型的指針
   TGetServerVariableProc GetServerVariable; /*檢索服務器變量值的回調函數指針*/
   TWriteClientProc WriteClient; //寫數據給用戶的回調函數的指針
   TReadClientProc ReadClient;  //檢索用戶數據的回調函數指針
   TServerSupportFunctionProc ServerSupportFunction;  /*支持其他操作的回調函數指針*/
   }

這種結構包含服務請求和回調函數指針所需得信息,你可以調用它來獲取信息或執 行操作,下面對其中的回調函數作一說明:

(1)GetServerVariable函數

原型為:

typedef BOOL _stdcall (*TgetServerVarableProc) (int hConn,*VariableName, void *Buff,int &Size);

調用這個函數來獲取服務器變量(如CONTEXT_TYPE)和同請求一起收到的頭部。如通 過請求, 則得到HTTP_COOKIE來檢索Cookies頭部的內容。參數說明:hConn為傳入參數 ECB的連接句柄ConnID;VariableName為要檢索的變量的名字(如HTTP_COOKIE);Buffer為 接收變量的緩沖區指針;Size為緩沖區大小,若由於緩沖區空間不夠而失敗,該值被改 變為必要的緩沖區大小。

(2)WriteClient函數

原型為:

typedef BOOL _stdcall (*TWriteClientProc) (int ConnID,void *Buffer,int & Bytes,int dwReserver)

調用這個函數來發送響應內容給用戶, 參數說明:ConnID為傳入參數ECB中的連接 句柄ConnID; Buffer為包含寫數據緩沖區的指針; Bytes為緩沖區數據的字節數; dwReserver保留。

(3)ReadClient函數

原型為:

typedef BOOL _stdcall (*TReadClientProc) (int ConnID, void *Buffer,int & Size)

調用這個函數讀取用戶的附加數據,通過檢驗ECB中cbAvailable和cbTotalBytes的 值來確定是否調用此函數, 若cbTotalBytes大於cbAvailable;就表明有更多的數據需 要調用該函數去讀取。 參數說明:ConnID為傳入參數ECB中的連接句柄ConnID;Buffer 為讀入數據存放的緩沖區;Size在調用時,傳入Buffer緩沖區的大小,返回時,等於實 際讀取的字節數。

(4)ServerSupportFunction函數

原型為:

typedef BOOL _stdcall (*TServerSupportFunctionProc) (int hConn, int HSERRequest,void buffer,int &Size,PDWORD DataType);

這個函數實現其他一些操作,參數說明:hConn為傳入參數ECB的連接句柄ConnID, HSERRequest為要實現操作的常量值。Size為Buffer緩沖區的大小,Buffer緩沖區指針; DataType為數據類型指針;其中Buffer和DataType的含義根據HSERRequest的值變化。

下面說明這個函數的幾個主要操作(也就是HSERRequest的可用值,在Isapi2.hpp中 定義),以及對應不同的操作,參數Buffer,Size,DataType的不同含義:

●HSE_REQ_SEND_URL_REDIRECT_RESP: 重定向客戶浏覽器到另一個網址上的URL。 Buffer:指向一重定向目標URL字符串;DataType被忽略。

●HSE_REQ_SEND_URL: 重定向到本服務器上的一個URL,Buffer: 指向一重定向目標 URL字符串;DataType被忽略。

●HSE_REQ_SEND_RESPONSE_HEADER:發送響應頭部給用戶;Buffer: 指向包含頭部的 字符串;DataType被忽略。

●HSE_REQ_DONE_WITH_SESSION: 通知服務器, 異步的請求處理已經完成。 Size, Buffer,DataType均被忽略。

●HSE_REQ_MAP_URL_TO_PATH: 映射一個邏輯路徑到一個物理路徑。Buffer:映射在 此緩沖區上完成;DataType被忽略。

HttpExtensionProc()函數的返回值必須使以下四個值(在Isapi2.hpp中定義)中的一個:

●HSE_STATUS_SUCCESS:所有進程已完成。

●HSE_STATUS_SUCCESS_AND_KEEP_CONN:所有進程已經完成,但希望保持連接以繼 續進一步的交互。

●HSE_STATUS_PENDING: 進程未完成。 當擴展異步完成進程時, 將以參數 HSERRequest=HSE_REQ_DONE_WITH_SESSION調用ServerSupportFunction(),以提醒服務 器進程已完成。

●HSE_STATUS_ERROR:進程由於錯誤已異常終止。

清單3包含了處理一個"Hello World"網頁請求的簡單 但必要的邏輯。

---------清單3------------------------

int _export WINAPI HttpExtensionProc(Isapi2::TEXTENSION_CONTROL_BLOCK &ECB)
   {
   char my_string[256];
   int length;
   strcpy(my_string,"200 OK/r/nContext-Type:text/html");
   length=strlen(my_string);
   ECB.dwHttpStatusCode=200;
   ECB.ServerSupportFunction(ECB.ConnID, HSE_REQ_SEND_RESPONSE_HEADER, my_string,length,NULL);//發送頭部  strcpy(my_string,"Hello World!");
   length=strlen(my_string);
   ECB.WriteClient(ECB.ConnID,my_string,length,0); //發送數據給客戶浏覽器
   return(HSE_STATUS_SUCCESS);
   }

這是一個簡單的例子,實際應用的ISAPI擴展將需要做比這更多的工作。

與GetExtenVersion()函數和TerninateExtension()函數不同,HttpExtensionProc ()函數對用戶的行為產生作用。

1.3 TerminateExtension()函數

TerminateExtension() 函數在用戶將卸載DLL時被調用,它是可選擇的。傳入參數 為dwFlages,類型為int,是以下兩個值(在Isapi2.hpp中定義)中的一個:

●請求同意卸載DLL的HSE_TERM_ADVISORY_UNLOAD值。 函數返回true將允許服務器 卸載該DLL。

●強迫DLL清除並准備被卸載的HSE_TERM_MUST_UNLOAD值。 TerminateExtension()函數對服務器行為產生作用。

編寫完ISAPI擴展應用程序後,用C++ Builder的Project->菜單項功能,為ISAPI擴 展生成一個DLL,這個DLL就可以直接被作為ISAPI擴展使用。

2.結束語

本文介紹的是一種在C++ Builder開發環境下較為復雜的ISAPI擴展的實現方法,這 種方法對理解ISAPI擴展的工作方式有很大幫助,除此以外,在C++ Builder中有更簡單 的方法來實現, 即通過使用WebModule, 以及TISAPIApplication、 TISAPIRequest、 TISAPIResponse等類。詳細方法參見C++ Builder文檔。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved