发信人: tgm98()
整理人: girlrong(1999-11-13 15:17:12), 站内信件
|
这是我在写ISAPI时的心得,请各位指教
利用HTTP 协议(1.0 ver以上)进行连续不间断的通讯
一、总述:
在平常的应用中,CLIENT和SERVER间的通讯经常要求在一开始连通后,一直保持 连接,这样可使得SERVER方可以主动发信息给CLIENT端,而不需要CLIENT以轮询 的方式定时去与SERVER沟通,以取得SERVER发来的信息。这种方式用TCP/IP的SO CKET编程是很容易实现的,因为SERVER端可以用一个单独的SOCKET来保持与CLIE NT的连接,只要双方的SOCKET没有关闭,任何一方均可主动发送信息。但HTTP的 连接方式是一次性的,因此,要让SERVER端主动发送信息就比较困难,经过仔细 探索,发现可用以下两种方法来解决这个问题。
二、所采用方法的软件平台及协议的版本:
WEB SERVER :IIS 3.0 以上
HTTP :1.0 以上
BROWSER:IE 4.0以上
三、解决方法
方法一:
在telnet中,用get 方法,取得站点的输出,便可得到所有基于TCP/IP的回复信 息,这样可以观察到HTTP的头信息等,便于研究服务器发送回数据的全过程。
从以前的经验知道,从服务器的ISAPI程序中返回信息给CLIENT一般是采用*pCtx t << _T(strRet),其中strRet为SERVER要发送的信息,但这种方法有一个特点, 即IIS先把strRet放在某一BUFFER中,必须等处理函数RETURN时,IIS才把BUFFER 中的strRet信息发送到CLIENT去,这样,无论你在函数中是分开发送的(亦即有 时间间隔的),但对CLIENT来说,总是一串连续的数据流一次接受完成。
那么,只要找出一个方法,能使得在SERVER的处理函数中及时把信息发送(即不 经过BUFFER的延迟),这样就可构造循环来保持与CLIENT的连接,并可在循环中 不断而及时地把信息发送回CLIENT,这个方法就是pECB->WriteClient( pECB->C onnID,szContent, &dwSize,HSE_IO_SYNC),如果ISAPI是用MFC构造的,则可用p Ctxt->m_pECB-> WriteClient( pCtxt->m_pECB->ConnID,szContent, &dwSize,H SE_IO_SYNC)。下面实现连续的HTTP通讯:
1、在客户端建立两个线程,一个用于接受数据,一个用于发送数据,接受数据的 线程一旦连上,就不再断开,以循环的方式使线程一直存在,而发送线程则每次 都重新连接,亦即发送完本次数据后,线程结束。
2、在服务器端也建立两个线程,一个用于接受数据,一个用于发送数据,发送数 据的线程一旦连上,就不再断开,线程一直存在,而接受线程则每次都重新连接 ,亦即接受完本次数据后,线程结束。接受来的数据当然要放在一个已建立的PU BLIC BUFFER ARRAY中,以便各个线程都可以取用。
但这样的方法有一个缺点,即服务器端的开销较大,因为,对每个已连上的客户 ,至少要有一个线程与其保持连接,这样,如果有100个客户同时上来,则必须至 少100个线程在与其连接,如果客户发送数据,则还要针对每个客户另开一个线
方法二:
如果能象TCP/IP得到SOCKET那样,得到一个指向连接的指针(或句柄,对象), 并且在SERVER的处理函数返回时,不断开与CLIENT的连接,该指针保持在全局BU FFER里,使得SERVER要主动发信息给CLIENT时,可取得该指针所指向的连接通道 ,进行通讯。这样,就可以和TCP/IP一样通讯了。
在HTTP 协议1.0 VERSION 以上,提供了KEEP-ALIVE的不中断连接方式,其本质是 一次HTTP连接后,SERVER 和CLIENT 间的TCP/IP连接仍然保持,下一次的HTTP请 求用的仍然是该TCP/IP通道。
经过测试,既得到了一个指向连接的指针,并且该指针能保留到全局BUFFER里, 下面是测试程序:
//KEEP.DLL
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>
#include <stdio.h>
EXTENSION_CONTROL_BLOCK* pECBRe=NULL;
int iCount=0;
BOOL WINAPI
GetExtensionVersion( OUT HSE_VERSION_INFO * pVer )
{
pVer->dwExtensionVersion=MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_M AJOR);
strncpy( pVer->lpszExtensionDesc, "ISAPI Keep-Alive Sample", HSE_MAX_EXT_DLL_NAME_LEN );
return TRUE;
}
DWORD WINAPI HttpExtensionProc(IN EXTENSION_CONTROL_BLOCK * pECB )
{
char szHeader[] =
"Connection: Keep-Alive\r\n"
"Content-Length: %lu\r\n"
"Content-type: text/html\r\n\r\n";
char szContent[] =
"<html> <form method=get action=\\Keep.dll>"
"<h1>First Sample</h1><hr>"
"<input type=submit value=\"Send Request\"></form></html>"
char szBuffer[4096];
HSE_SEND_HEADER_EX_INFO HeaderExInfo;
DWORD dwSize;
sprintf( szBuffer, szHeader, strlen( szContent ) );
HeaderExInfo.pszHeader = szBuffer;
HeaderExInfo.cchHeader = strlen( szBuffer );
HeaderExInfo.pszStatus = "200 OK";
HeaderExInfo.cchStatus = strlen( HeaderExInfo.pszStatus );
HeaderExInfo.fKeepConn = TRUE;
// char buffer[1024];
// DWORD dwbufferSize=sizeof(buffer);
// char ip[]="REMOTE_ADDR";
// pECB->GetServerVariable(pECB- >ConnID,(LPTSTR)ip,(LPVOID)buffer,&dw bufferSize);
iCount++;
if(iCount>1)
{
char szContent1[] =
"<html> <form method=get action=\\Keep.dll>"
"<h1>Second Sample</h1><hr>"
"<input type=submit value=\"Send Request\"></form></html>"
dwSize = strlen( szContent1 );
pECBRe->WriteClient(pECBRe->ConnID,szContent1,&dwSize, HSE_IO_SYNC );
}
pECB->ServerSupportFunction(
pECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER_EX,
&HeaderExInfo,
NULL,
NULL
);
dwSize = strlen( szContent );
pECB->WriteClient( pECB->ConnID, szContent, &dwSize, HSE_IO_SYNC ) ;
if(iCount==1)
{
pECBRe=pECB;
}
return HSE_STATUS_SUCCESS_AND_KEEP_CONN;
}
BOOL WINAPI
TerminateExtension( IN DWORD dwFlags )
{
return TRUE;
}
注解: 1、定义两个全程变量:iCount, pECBRe
2、在返回的HEAD中包含"Connection: Keep-Alive\r\n"
3、在处理函数返回时应返回
HSE_STATUS_SUCCESS_AND_KEEP_CONN
4、在这次测试中,有甲乙两台客户机参与。
A、甲发请求KEEP.DLL
B、KEEP.DLL返回包含有"Connection: Keep-Alive\r\n"的HEAD,
CONTENT,RETURN时返回
HSE_STATUS_SUCCESS_AND_KEEP_CONN
同时,把甲与服务器的用于连接的pECB保存在全程变量pECBRe
这样,甲与服务器的这一次请求完成后,仍保持着TCP/IP的连
接。
C、乙发请求KEEP.DLL
D、因为iCount的记数已大于1,所以,KEEP.DLL会把信息发送到
pECBRe所指向的连接,即服务器与甲的连接,并向甲发送信
息,这时,可从甲的浏览器上看到服务器发来的信息。
E、同时,KEEP.DLL也会发送信息回乙方。
F、当甲方按停止按钮后,KEEP.DLL中的pECBRe所指向的BUFFER
由IIS自动收回,以便分配给下一个连接使用。
G、要服务器方主动切断与甲的连接,目前测试了许多方法,均不
奏效。
H、如要转到基于MFC的DLL程序中,亦是一样的。
I、 如果客户端用WININET API写的程序,其连接原理也应是一致
的。
J、 按MICROSOFT的文档说明,如果客户方使用了PROXY
SERVER,且是MICROSOFT的PROXY SERVER,Keep-Alive
是允许的,即可通过PROXY SERVER。
-- 不要对我说生命中辉煌的事,
不要对我说失败是命运的事,
那些在经验里我只相信一次,
因为我和你一样,
要这样走过一生,
我只有低头前进。
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.96.190.64]
|
|