|
|
山东移动通信公司 郭彬 ( Y8 g) u/ }* v" s* V/ }4 U; {! i: T& ]
/ }+ d" \% U, {* g( V: z$ `---- 本文通过对CGI和ISAPI的对比以及对ISAPI的工作原理的分析,阐述了使用MFC ISAPI编程的一般方法。 - w) O& p" Y3 d! S
4 E7 a0 l6 [. _* H/ ~4 v
---- 一、ISAPI简介: 0 G9 w! K( k- Y4 e7 x2 t
* D4 }1 Q. F6 K+ D$ F) a2 Q
---- 目前大多数网站提供信息的方式是由用户通过客户浏览器(如Netscape或Explorer等)与WWW服务器连接,然后用鼠标点按超级连接以浏览相关的主页。在此过程中,基本上是服务器向客户端单方向的信息传递,随着服务需求的不断扩大,这种单纯的单方向传递信息的静态主页已不能满足需求。不论是服务提供方还是客户,都希望在浏览服务器将信息发送到用户同时,用户端也能将信息发送到服务器端,实现服务器与客户的交互。典型的应用如:用户登记表、用户留言板以及用户通过WWW检索服务器端的数据库等等。
- a- z* M! Q1 ?+ W% y: H( ^' _# x8 m$ D# C; y# u
---- 要实现一个可与用户交互信息的服务系统,所需增加的有两项:交互式主页和用户输入信息处理程序。交互式主页即有输入信息的编辑框,选择菜单按钮之类的主页,以供用户输入信息;用户输入的信息则交由用户信息处理程序处理。实现该程序可以有多种途径,该程序可以放在服务器端,也可以放在客户机端,前者如CGI、ISAPI,后者如JAVA Applet、javascript等等。 # t. ?% Q, I g+ C# D9 m
- `' G6 C& m" W
---- ISAPI(即Internet Server Application Program Interface),是微软提供的一套面向Internet 服务的API接口,它能实现CGI(Common Gateway Interface,公共网关接口)能提供的全部功能,并在此基础上进行了扩展,如提供了过滤器应用程序接口。
, D1 J) e. \( C }! G" M
% _6 r& R- |" H) |1 ~! h$ U( n# ?---- 在Web服务器这个竞争激烈的领域里,微软的IIS(Internet Information Server,Internet信息服务器)是当今Windows NT操作平台上执行效率最高的Web服务器之一。IIS提供所有传统的内容传递方式,它对静态网页有着良好的支持。对于更复杂的应用程序,IIS提供了功能更强的应用框架:使用ISAPI能够创造出极高性能的应用程序。书写良好的ISAPI扩展的性能可能超过类似的CGI一个数量级。此外,ISAPI的灵活性使一些事情变得简单。 0 J; w* c7 I$ |0 g7 z
6 A9 Q- {, t3 @8 R! |5 G- L---- ISAPI提供了扩展支持WEB服务器的简单而有效的方法。开发人员可以设计生成ISAPI 服务器扩展动态连接库(ISAPI Server Extension DLL),它可以被HTTP服务器调用。例如,客户端用户填写了一个表单,按下"提交"按钮后,输入的数据将被传送至HTTP服务器,激活相应的ISAPI扩展应用程序,该应用程序可以处理用户的输入信息,进行相应的操作。或者,按照用户的要求访问数据库,读取用户指定的数据,动态生成HTML文件,再传回客户端。 " x( c9 M$ V! K0 m" I' j
0 R* G* Y+ t! z0 _7 H# s9 L
---- CGI很早就作为交互式的Web应用程序的一个标准广泛应用在Internet之中。CGI脚本允许人们用多种编程语言如Basic、C、Perl、Shell 等等来编写简单的应用程序。这些脚本运行在Web服务器上,而在客户的Web浏览器上输出运行结果。客户的输入通过环境变量或者标准输入设备来进行传递,然后CGI程序根据需要完成特定的功能,并通过标准输出设备送回HTML格式的结果显示在客户的浏览器中。CGI的这一特性―设计简单,再加上它支持多种编程语言,使得开发CGI应用程序非常简单。尽管如此,人们在使用中还是发现了CGI应用程序的一个很大的缺点:性能不高。 虽然有不少办法来使CGI应用程序运行得更快一些(如把它们变成编译好的二进制代码,而不用Perl脚本),但执行速度仍然是一个问题。每当通过Web访问一个CGI程序时,CGI执行文件(或者脚本的解释器)都要为每一个请求创建一个新的进程。对于一个信息量比较大的站点来说,这无疑给服务器增加了一个沉重的负担。
$ n$ x9 }0 h' Z. ?* Y9 v* w
7 S& q( |% O9 Y' S4 f6 F8 A---- 二、ISAPI的工作原理: 2 _2 f% r( x9 B" L+ @
, @/ y: N# n* Q* A5 ~---- ISAPI的工作原理和CGI大体上是相同的,都是通过交互式主页取得用户输入信息,然后交服务器后台处理。但是二者在实现机制上大相庭径。ISAPI与CGI最大的区别在于:在ISAPI下建立的应用程序是以动态连接库(Dynamic Link Lib,DLL)的形式存在;而CGI的应用程序一般都是可执行程序。ISAPI应用的工作流程与CGI也有一些不同。ISAPI应用的DLL不仅可以象CGI程序一样被用户请求激活,还可以被系统预先激活来监视用户输入;对于被用户激活的DLL,在处理完一个用户请求后不会马上消失,而是继续驻留在内存中等待处理别的用户输入,直到过了一段时间后一直没有用户输入。
" H4 V9 N h3 S$ {( Q/ V% ?! |
+ f6 ]! j2 O. k) J% s& D6 W7 ]/ e8 a3 T---- 一个ISAPI的DLL,可以在被用户请求激活后长驻内存,等待用户的另一个请求,还可以在一个DLL里设置多个用户请求处理函数,此外,ISAPI的DLL应用程序和WWW服务器处于同一个进程中,效率要显著高于CGI。 9 L9 D( r2 S2 O; _6 U/ W" @7 F
7 V c8 B1 e, h) V- x---- 不过ISAPI的平台兼容性较差,目前只能用于微软自己的Windows 95和NT操作系统上,服务器平台也仅限于IIS(Internet Information Server)和MS personal web server以及NT workstation上的peer web server。 + E0 A ~3 H+ p' c, l6 {$ R5 s
8 q& M: a% ?, w9 O---- ISAPI DLLs的调用方法和CGI一样:在客户端使用GET或POST方法。例如,当客户作出下列请求时:
$ F* D1 \- a6 r5 o, Q) ]" V( Q t3 f7 y2 P* d$ B# R
---- ---- http://www.mysite.com/myisa.dll?name=fisherman&id=12345 "name"域和"id"域以及与它们相关的数据都被传递给ISA。ISAPI在使用这些相关的数据之前把它们存放在相应的数据结构中,这是通过一个请求映射系统来实现的。
( m7 s3 }/ o3 j& \3 |; [3 P: m8 }: t" R( Q9 w! E% r6 }
---- 每一个请求都有一个解析映射表。通过定义服务器从客户接收的数据的类型和顺序,该解析映射表可以把数据传递到合适的数据结构中。例如,对于请求"name=fisherman&id=12345",解析映射表将显示一个字符串和一个整型数,并且" fisherman " and "12345"将被解析出来存放到各自的数据结构中。 1 y: c$ N: o. G( F
. N! a2 v) c |- i8 t2 [8 q" E---- 解析映射系统还有另外一个功能:ISAPI可以把请求传递给ISA内特定的成员函数。请求字符串可以包含一个命令,解析映射表就使用该命令把请求传递给ISA内正确的成员函数。 ! g6 `' j( C, `1 d
: {1 s6 u) ~! i. K% f% h4 Z
---- 三、MFC的ISAPI编程方法:
) i6 X0 R) }4 ^- ]$ F) e" u3 I* a4 M3 D; b$ D/ j' I. H0 E) F( d
---- 1、用户输入的方法:
( J' d9 B. |8 V! T& q0 ?9 i' x! q, H& y9 n# j# |. s
---- Web表单: ! t; r5 X: O/ L" J, B; j4 O
: E0 i8 F# K, E# Y% C
---- Web表单(Form)提供了一种与用户进行交互的方法。它使用"打包"的技术,以收集用户的输入,触发网络服务器的行为,获取用户的响应,功能类似于Windows中的对话框。
+ ]3 z, H: E1 u/ n. j
$ A' v! f1 v7 N, R; T4 R---- 表单使用
3 R2 U0 G" D( ~( ^和
1 H# U1 ^6 ^8 c4 \# g标识定义了一个组成表单的HTML网页的区域。当选择了表单中的Submit按钮,两个标识之间的数据输入就被作为一组发送到服务器。典型的设计如下所示: - x# z6 F" a; {) w5 Y1 T& u( d
& u @/ f4 s" m! o2 L% t7 r4 Z, O% \< form ACTION="input.dll" METHOD="POST" >
9 i8 m% z2 p C< input TYPE="hidden" NAME="MfcISAPICommand" value="Input" >
4 D# b" @+ {. N.
}9 i% p! ?' T; u% e4 M+ m' U< input [attribute-list] > x2 ] \) n+ V2 u/ o; h* w! S. b
.% I, o) s9 C' ~) L$ C' b
< input TYPE="submit" value="OK" > ! Z4 Z" C: \" O
< input TYPE="reset" value="Reset" > 8 [: {; y7 k0 f/ J6 [+ @
< /form >
# ?$ s! `4 g( y5 N! B1 P }+ W2 W* C
---- 其中"ACTION"属性指定了ISAPI DLL在服务器上的存放位置。"METHOD"属性指出了数据将被传送到服务器的方式:如果是"GET"方式,数据就作为URL的一部分被编码并发送到服务器;如果是"POST"方式,数据就作为消息的主体被发送。 - C0 S( i3 C3 L n. Y
---- < input >标识符用于从用户收集输入,输入类型由"TYPE"属性决定。由标识符支持的输入类型如下:
/ E9 C9 ?; Y3 ?7 B' F* `0 Y1 t
1 x( l- L% Q3 z) k0 v7 }¨ 文本输入
/ k. j' L+ H7 A6 ~ \' u¨ 口令输入8 K# [' f: T7 `1 Y& V
¨ 单选按钮
8 P0 S5 h6 q+ e+ `¨ 复选框
+ j; z4 V% Q0 `. P# W¨ 图象输入2 f/ a7 L9 S) g8 A; Z
¨ 隐藏域
1 Y& f v3 x" @4 b- k2 {1 f ]# x/ g¨ 提交按钮
8 w3 }% F; `4 R¨ 重置按钮
1 Q# g; m' p: H9 k1 L5 T/ p
9 T/ I, h6 t* R& | P4 h---- 这些输入单元都有以下的形式:
) Y( x8 `; j8 R. v% S< input type="text" name="EquipName" value="MSC" >
+ Z6 e1 [/ x/ Y& S
; E4 W/ T( E. y6 }& E% u---- "name"是给输入单元一个标识,这个标识将作为ISAPI DLL中响应函数的参数。"type"是输入单元的类型,用以区分以上几种输入单元。"value"设置输入单元的初始值。
8 q( H8 H, u7 |1 R# A) J, o7 K---- 注意上面的HTML源程序,在类型为"hidden"的输入单元中,定义了服务器端响应此用户输入的函数名。而类型为"submit"和"reset"的两个输入单元是两个按钮,当用户按下"submit"按钮,系统就将输入的内容发送到服务器端;当用户按下"reset"按钮,系统就将用户输入的内容重置为原来的默认值,以便重新输入。 : C7 u5 x' ?+ _: k
9 u$ l6 u; `$ [ N4 |$ d. Q6 ~) J---- 超级连结: : U- \3 s& _2 x G+ a
# y- k. p1 C# i3 \8 W; }---- 用户也可以使用超级连结的方法来激活一个ISAPI DLL。如下所示:
' W8 N" U1 i* ^5 h; l
4 H- E/ k; S) I< a href="browser.dll?mfcISAPICommand9 O4 k) S# J1 y1 X; w% n6 i
=Browser" >Browser< /a >
% `9 A$ E- X4 T9 n B0 N" b& @% d' l1 S4 G
---- 这里,隐含地采用了"GET"的方式。在href中指明了ISAPI DLL的模块名和具体的函数名。当用户点中了这个超级联结,系统就会将用户的请求发送到服务器。 8 B8 h$ @% R5 t! A7 k1 u3 ]1 v
---- 2、MFC中对用户数据输入的响应方法: - ^5 w: r. b6 m( ^6 A6 }
: M+ ~0 o, _" [+ ]
---- 从发布开始,MFC(Microsoft Foundamental Classes,微软基础类库)就被认为是开发Windows应用程序的标准平台。通过提供更清晰、更简单的Windows API接口,MFC可以帮助开发者更快、更简单地编写程序。 ; j* Q) `1 N& ~& [) d
- H# ~& i' B! J0 D4 r8 |0 V/ K9 f---- MFC中支持ISAPI的类有: 9 E' z) v2 K: O7 R( \! r3 ?" t5 |
6 a& f, ]# @) z: j¨ CHttpServer
- q6 d/ Q* p5 j/ |6 u6 F¨ CHttpServerContext: w% x( [' ~" C# l
¨ CHttpFilter
3 B! p" k: H7 K( ~# m¨ CHttpFilterContext
5 P" L# R* I) T( {# x¨ CHtmlStream
) ?" r/ b: X& O2 w- l9 a2 v
% F9 E. o; W1 B---- 用户可以方便地使用"ISAPI Extention Wizard"来生成一个ISAPI DLL的基本框架,然后只要添加自己需要的函数就可以了。 / ^2 @3 Q& D" L0 c, r2 j, S
---- HTML的用户输入和命令处理例程是通过一个叫做"MFC PARSEMAP"的数据结构来实现解析映射的。在生成的框架中,可以看到形如以下的源程序:
5 m i: G/ r7 b/ j9 P- a4 O+ I( x& s `% I- f0 K2 U. T
BEGIN_PARSE_MAP(Cbrowser
# ~& L( l; _1 D0 |0 ?) Q: @Extension, CHttpServer)1 X4 V0 V; `% \/ B, I/ y
ON_PARSE_COMMAND(Browser,9 U3 x+ b X; N$ C2 m: F; ]
CBrowserExtension, ITS_EMPTY)" {- ]' p+ f l+ j6 T9 ~
DEFAULT_PARSE_COMMAND(Browser,% s; E- a5 B1 x4 f3 v
CBrowserExtension)
. E9 d) \9 S2 q M* ?8 i) A4 T# Z
ON_PARSE_COMMAND(Query,
' L9 }0 l! w/ P1 r( p$ zCBrowserExtension, ITS_PSTR), c' X4 \, B u8 r( y K2 j! r
ON_PARSE_COMMAND_PARAMS("OfficeName")
- Q& X; z! h1 O2 v* ^' GEND_PARSE_MAP(CBrowserExtension)) v- @( X, }" U% U
9 Y: P4 f: x# o9 }2 K
它们的含义如下:
* M( q7 }, Z; t. kBEGIN_PARSE_MAP(Cbrowser
; g. }$ Q$ }8 F. d0 X' i5 TExtension, CHttpServer)和; u7 M1 F1 i7 s3 d' y, E% {& j" c
END_PARSE_MAP(CBrowserExtension):
6 i3 M8 c/ U' M, V7 t! {+ X8 N
+ H9 B1 Y; h% f3 f6 e L# |---- 定义PARSEMAP的开始和结束。并且指出本DLL所使用的类名及其基类名(一般是CHttpServer)。 8 \0 r9 R2 D I3 H/ s( i, O
---- ON_PARSE_COMMAND(funcName,className,paramList)
5 r! a7 v; C% s2 L# u5 e4 o" L6 p4 n0 Q
---- 定义支持服务器侧接收用户输入的一个函数,以及它所接受的参数。该函数与WEB表单的隐含输入域中定义的"value"值要一致。用户只要编写此函数,就可以实现对用户输入的响应。必须要保证这里定义的参数与WEB表单中定义的输入单元是一一对应的。 ' i; Z) i. F9 B6 Z7 I
6 d: p, u5 i D( T( q% e: H& E---- ParamList是一个或多个代码定义的函数的参数列表,可用的代码如下:
+ R- }7 F/ a( ~" w
4 c, M# ?" N6 D m. ] E! W代码 含义 C++中与之对应的数据类型- u1 \' Y& b* j
ITS_PSTR 字符串 LPCSTR或char * const, }8 t; _1 R$ U0 A5 \7 |- e
ITS_I2 2字节整数 Int# f. T) j/ N: i$ u- {
ITS_I4 4字节整数 long int
6 v4 q/ l: w$ [9 V! LITS_R4 4字节浮点数 Float2 f }% J0 n- q
ITS_R8 8字节浮点数 Double5 O5 m3 b: a6 x4 k3 G
ITS_EMPTY 空的参数列表
: p0 G% b W9 I3 j
* z, I8 `8 q' j( }9 J0 nON_PARSE_COMMAND_PARAMS
! |. L$ c3 i8 K, j% _8 A3 b6 ]
9 Z$ J q4 l* y4 B---- 该宏定义参数的名称,注意要与HTML的表单中各个输入单元的"name"值保持一致。
* ^& h. y) G$ K4 P8 b! V# X# b---- DEFAULT_PARSE_COMMAND
6 f$ [0 ~! C* K" F# S3 V5 P9 W( u+ p9 @6 g; @. ?. a
---- 用于指定如果浏览器传送的表单或超级连结中没有包含一个函数名时,被DLL缺省调用的函数名。
3 \3 i2 t" q2 j9 @0 P! G* L" Z
3 {& x. U/ J3 g9 o" _+ m. w---- 3、响应函数的结构:
4 O( b% p) _9 s4 {* J; Y. d+ }2 t5 B ~0 o' r+ p% X' S, E
---- 响应函数形如以下结构: 1 I' @( W% A+ D& X* e
/ O3 S6 h' W! K2 Ovoid CBrowserExtension::Browser* ~3 u. }- q4 X$ w
(CHttpServerContext* pCtxt,…)
+ y. Z( H( c' T$ v7 {0 A. U{
% w$ d+ W- e+ ]+ b0 SStartContent(pCtxt);( r! l' B$ F% ^5 I+ N, Z( ]
*pCtxt << "< p >Hello World !< /p >";
; O d% m5 A2 s//其它的处理。' S W) M% x9 e4 M0 ]$ ~
EndContent(pCtxt);
9 V$ [9 \: g/ a}
1 t! p: N0 ^# I; i+ I2 m) ]/ w: C
) e2 J; `# E0 A4 T8 B9 Z: h---- 响应函数总是以StartContent(pCtxt)函数开始,以EndContent(pCtxt)函数结束。*pCtxt的作用是向用户端传送数据。
5 Y9 W X- @- F: K0 P$ G---- 服务器可以对输入参数进行相应处理,并将处理结果以HTML的形式发送回用户端。所以要在此函数中,要将需要在用户端显示的整个网页的HTML源代码发送过去。虽然略显麻烦,但增加了灵活性,可以按照用户的输入来动态地生成各式各样的网页。
* `, F" k# A2 V% @
- t/ c+ e1 V/ L+ J, y---- 使用ISAPI,可以使得静止的网页立刻变得丰富多彩起来! |
|