概述
本文着重介绍了在1.1SDK中引入的IWEB接口,以及相关的IWebResp、IWebOpt、IwebUtil等API的使用方法。也包括各API的功能和与其他API(例如IHTMLVIEWER)的交互。
―――――――――――――――――――――――――――――――――――――――
基本作用
在BREW应用中,IWEB接口主要用于发起HTTP请求。在HTTP请求中,既可以通过“GET”方式获取文件,也可以通过“POST”方式上传数据,这两种方式IWEB都支持。IWEB管理自身的连接和套接字,因此更利于网络传输。
IWEB_GetPesponse函数用于开始网络传输。
Void IWEB_GetResponse(
Iweb * pIWeb,
IwebResp ** ppiwresp,
AEECallBack * pcb,
Const char * cpszUrl,
...
)
其中:
pIWeb 指向一个已实例化的IWeb对象的有效指针。
ppiwresp 指向IwebResp指针的有效指针。
pcb 指向一个已实例化的AEECallBack对象的有效指针。
cpszUrl 指向URL的指针。
... 是WebOpt中各项的名称和取值,成对排列,以WEBOPT_END结束。
注意:IwebResp指针不可以创建在在栈里。为了保证它在整个回调的过程中一直有效,创建在应用对应的结构体里比较好。
URL不可以包含任何保留字符。如果必须包含,请注意转换。详见本文 “在URL中使用保留字符”部分。
用IWeb请求一个文件,最简单的形式如下:
//Create IWeb instance
ISHELL_CreateInstance(pMe->a.m_pIShell,AEECLSID_WEB,(viod**)&pMe->m_pIWeb);
//Initialize the callback to WebReadCB
CALLBACK_Init(&pMe->m_Callback,WebReadCB,pMe);
//Request IWEB to fetch the URL
IWEB_CetResponse(pMe->m_pIWeb,(pMe->m_pIWeb,&pMe->m_pIWebResp,&pMe->m_Callback,http://www.qualcomm.com,WEBOPT_END));
注意此函数中参数传递的特殊语法。
IWEB和服务端URL“www.qualcomm.com”建立一个网络连接,并请求得到文件。当IWEB引擎获得了来自服务端的响应,它会通过pcb传递给AEECallBack中的回调函数。如果请求文件的方式没有指定,默认为“GET”。请求文件的方式以及其他选项的设置方法,参见本文的“IWEB_GetResponse请求中WebOpts的设置”。
WebRespInfo结构体中包含了响应的消息体和其他相关信息(比如错误号、长度、类型等),所以,可以通过IWEB_GetInfo函数解析WebRespInfo取得响应的内容。具体参见本文的“如何理解WebRespInfo中的错误号”部分。
//Callback
static void WebReadCB(void * cxt)
{
...
//get information about the response
WebRespInfo * pWebRespInfo = IWEBRESP_GetInfo(pMe->m_pWebResp);
//the body of the response is contained in the ISOURCE
ISource * pISource = pWebRespInfo->pisMesssage;
}
―――――――――――――――――――――――――――――――――――――――
IWEB_GetResponse请求中WebOpts的设置
前面,我们已经了解用IWEB开始一个简单的HTTP传输的方法,现在通过学习WebOpts的使用,将我们对IWEB接口的了解深入一步。
一个WebOpt包含两项内容:选项名称及其取值。这些WebOpt应该被加入到IWeb_GetResponse函数中,用于配置将要发出的请求。WebOpt可以以两种方式添加:
使用IWEB_AddOpt
以这种方式添加的选项是永久的——只要IWeb对象没有释放,这个设置一直有效。
int i = 0;
WebOpt awo[10];
//Add handler data
awo[i].nId = WEBOPT_HANDLERDATA;
awo[i].nVal = (void*)pMe;
i++;
//add the handler function for status callbacks
awo[i].nId = WEBOPT_STATUSHANDLER;
awo[i].nVal = (void*)WebStatusNotification;
i++;
//add the option stating which method
awo[i].nId = WEBOPT_METHOD;
awo[i].nVal = (void*)”GET”;
i++;
//marks the end of the arrary of WebOpts
Awo[i].nId = WEBOPT_END;
//add options
IWEB_AddOpt(pApp->m_pIWeb,awo);
IWeb_GetResponse(pMe->m_pIWeb,(pMe->m_pIWeb,&pMe->m_pIWebresp,&pMe->m_Callback,”thhp://webber.qualcomm.com”,WEBOPT_END));
直接添加
WebOpt的选项名和值可以在IWeb_GetResponse函数中直接设置:
IWeb_GetResponse(pMe->m_pIWeb,(pMe->m_pIWeb,&pMe->m_pIWebresp,&pMe->m_Callback,”http://webber.qualcomm.com”,
WEBOPT_HANDLERDATA,pMe,
WEBOPT_STATUSHANDLER,WebStatusNotification,
WEBOPT_METHOD,”GET”,
WEBOPT_END));
这种方式添加的设置,只在这个IWeb_GetResponse中有效。
注意:不管以哪种方式添加,请求的设置应该在整个回调的过程中保持有效。若想设置被自动复制到需要的地方,在选项列中添加WEBOPT_COPYOPTS。
有时候,AEEWeb.h文件中的解释比API帮助中的更加详细。常用的WebOptions和解释如下:
WEBOPT_METHOD:设定使用“GET”或者“POST”方式。
取值:GET、POST。
WEBOPT_BODY:用于设置POST的消息体。
WEBOPT_CONNECTTIMEOUT:设置在尝试成功建立连接的过程中,IWeb需要等待的时间。时间到则放弃连接,并返回连接错误号。以毫秒计算,默认值为AEENet的连接时长。设为0表示使用系统默认值,-1表示无穷大。这个值要足够一次成功的连接。假设成功建立了连接,服务器响应却比较缓慢,在这个过程中,IWeb不可以报“连接超时”。可以根据需要使用IShell_SetTimer。
WEBOPT_IDLECONNTIMEOUT:一个空闲连接保持的时间,此时间到,则关闭连接。默认值为InetMgr中PPP的连接时间,设为-1表示无穷大。
WEB_HEADER:设置特殊的http消息头,由CRLF组成。CRLF是指单独列出的名称和取值,例如:”Name1:v1\r\nName2:v2\r\n”。
WEBOPT_PROXYSPEC:用于设置代理。详见《BREWWebProxySpecKnowledgeBase》。
WEBOPT_FLAGS:这个设置会影响底层协议引擎的行为。这些标志使我们在使用连接时取得更多的控制权。已实现的标志如下:
WEBREQUEST_NOKEEPALIVE:IWeb不会刻意地保持连接。在数据交换结束后立即释放连接。默认情况下,IWeb尽力保持连接。
WEBREQUEST_FORCENEWCONN:在已经和某服务器已经建立连接后,每次连接这个服务器,IWeb都会重新建立一个连接,而不沿用以前的连接。这样做,您将承担以下风险:手机上没有空闲套接字可用、传输失败。默认情况下,IWeb会重复利用已有的连接。
WEBREQUEST_NOWAITCONN:如果没有可用的连接,建立新连接。通常情况下,如果已有一个连接正在工作,IWeb会等待这个连接释放,然后建立新的。如果设置这个标志,IWeb会立刻建立新连接。
其他选项的详细信息,参见AEEWeb.h。
―――――――――――――――――――――――――――――――――――――――
如何理解WebRespInfo中的错误号?
发送请求给服务器,得到服务器的响应,我请求的结果在那里?上面提到过,回调中包含一个指向IwebResp对象的指针。可以通过如下方法从IwebResp中得到信息:
//callback
static viod WebReadCB(viod*cxt)
{
…
//get information about the response
WebRespInfo * pWebRespInfo = IWEBRESP_GetInfo(pMe->m_pIWebResp);
…
}
pWebRespInfo->nCode包含响应的状态:成功或者失败。正数1代表由服务器返回,负数代表系统错误,参见AEEError.h。为了理解nCode的意思,可以使用以下宏:
WEB_ERROR_SUCCESSED(nCode):这个宏在传输成功时返回TRUE。
WEB_ERROR_MAP(nCode):如果是HTTP错误,返回WEB_ERROR_PROTOCOL系列错误号。错误号对应的意思参见AEEWeb.h或者RFC2626。
检查响应的状态和消息头
如果有需要,查看来自服务器的响应的消息头是很容易实现的。在响应中设置WEBOPT_HEADERHANDLER和WEBOPT_STATUSHANDLER即可。这些选项为相应的消息头和状态设置了回调函数。
用法:
IWEB_GetResponse(pApp->m_pIWeb,(pApp->m_pIWeb,&pwa->piWResp,&pwa->cb,pszUrl,
WEBOPT_HEADERHANDLER,WebAction_Header,
WEBOPT_STATUSHANDLER,WebAction_Status,
WEBOPT_END));
WebAction_Header属于PFNWEBHEADER类型,WebAction_Status属于PFNWEBSTATUS类型。更多信息参见API帮助文档。
―――――――――――――――――――――――――――――――――――――――
发起HTTP的POST请求
HTTP的POST请求是指:把数据上传到服务器。IWeb要先准备好上传的数据流。
// set up the callback
CALLBACK_Init(&pMe->m_Callback, WebReadCB, pMe);
// Create a Source Util object which will create an ISource object
// from a buffer, file, socket, etc.
ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_SOURCEUTIL, (void
**)&pMe->m_pISourceUtil);
// Create ISOURCE object
ret = ISOURCEUTIL_SourceFromMemory(pMe->m_pISourceUtil,
pMe->m_szData, // data in buffer
pMe->m_nContentLength, // length of data
NULL,// No callback
NULL,
&pMe->m_pISource //object to be created
);
// Kick off the transaction
IWEB_GetResponse(pMe->m_pIWeb,
(pMe->m_pIWeb,
&pMe->m_pIWebResp,
&pMe->m_Callback,
pMe->m_pURL,
WEBOPT_METHOD, "POST", // Set method to POST
WEBOPT_BODY, pMe->m_pISource, // Set body of message
// to ISOURCE object
WEBOPT_CONTENTLENGTH, pMe->m_dwContentLength,
WEBOPT_END));
跟“GET”请求类似,回调函数必须有正确的代码和数据才可以被调用。
注意:在BREW1.x和2.0中,如果POST的消息体很长会出问题。
―――――――――――――――――――――――――――――――――――――――
在URL中使用保留字符
在HTTP请求中,URL中不能含有保留字符。如果不可避免,在发出请求之前,URL得被译码成为可接受的格式——使用IWEB_UrlEncode。
char * IWEBUTIL_UrlEncode
(
IWebUtil * pIWebUtil,
const char * cpcIn,
int * pnInLen,
char * pcOut,
int * pnOutLen
)
这个方法可以根据RFC2936译出一个包含URL的字符串。
注意:只对URL后缀中的保留字符重新译码,而不是整个URL。
假设URL像这样:“http://www.qualcomm.com/test?param1=x”只译码“test?param1=x
”部分。用例如下:
// Determine what the length of the output buffer needs to be
IWEBUTIL_UrlEncode(pMe->m_pIWebUtil,
(const char *)(buf), // buffer containing the suffix
// of URL with reserved chars
&nDataLen, // number of characters to be
encoded
NULL, // if NULL, method calculates
//size of buffer needed
&pMe->m_dwContentLength // contains the length
// of the buffer required
);
// Allocate space to hold the encoded string
if(pMe->m_dwContentLength)
pMe->m_szData = (char*) MALLOC(pMe->m_dwContentLength);
// Encode the string.
IWEBUTIL_UrlEncode(pMe->m_pIWebUtil, (const char *)(buf), &nDataLen,
pMe->m_szData, &pMe->m_dwContentLength);
译码后的字符串类似于“test%3Fparam1%3Dx%0”。
m_szData包含译码后的URL后缀。需要和URL的前部分连接起来,才可以用于IWEB_GetResponse。
保持连接和连接缓存
HTTP1.1引入了通过Keep-Alives使连接持久的概念。IWeb利用了这一特点,并作为默认设置。用WEBREQUEST_NOKEEPALIVE WEBOPT_FLAG可以取消。
如果应用请求服务器www.qualcomm.com/index.html,IWeb建立一个到此地址的连接。请求得到响应后,IWeb并不关闭连接,而是加入缓存。如果又有需要连接到同一服务器的请求,例如www.qualcomm.com/developer.html,IWeb会重用这个连接。这一功能可以通过WEBOPT_FLAGS关闭。
尽管如此,持久连接也是受限的。Keepalives取决于消息头中Content-Length的内容。在一个有效的“Content-Length:xxx”消息头中,只能包含“Connection:Keep-Alive”。如果想告知对方何时停止读数据流,就得传送内容长度(不送Connection:Keep-Alive),连接就断了。
由于很难或者说根本不可能提供一个“Content-Length:xxx”的有效消息头,大部分服务器在提供了CGI或其他动态内容后,回到“Connection:Close”。
就算有效的内容长度消息头存在,服务器也可以在任何时间关闭连接。这是HTTP标准的一部分。根本没有办法来确保一个持久的连接。
―――――――――――――――――――――――――――――――――――――――
巧用IWEBUTIL_ParseFormFields
当应用中需要收集用户的信息并把它传送到服务器时,这个方法特别好用。
BREW应用可以利用IHTMLViewer接口在手机上显示HTML表格。用户可以在这个表格里输入必要的信息。这些信息可能要被传送到服务器,也可能应用要利用这些信息实现用户要求的功能。
比如,一个文本框用于用户输入铃声的名字。这个信息也许会被传送到服务器去查找相应的铃声文件。在同一张表中,也许还有个音量的多选框,让用户选择铃声播放的音量。这个信息不必送到服务器,应用会根据它来改变自身相应的功能。
当用户点表单的提交按钮时,HVN_SUBMIT就会连同URL一起,被传送给HTMLViewer的回调函数。其中,URL的后缀包含表域及其内容。
name1=value1&name2=value2&name3=value3& ...
或
name1=value1;name2=value2;name3=value3; ....
IWEBUTIL_ParseFormFields可以从这个后缀中得到各表域和值,具体如下:
假设传递到回调函数的URL如下:
request:ringer?ringer=Sacrifice&volume=50
//find the suffix
pszIter = STRCHREND(pszIter, '?');
if (*pszIter)
++pszIter;
{
IWebUtil *piwu;
WebFormField wff[4]; // Create an array of WebFormField structs
ISHELL_CreateInstance(pApp->a.m_pIShell, AEECLSID_WEBUTIL,
(void **)&piwu)) ;
// This populates the WebFormField structs with a maximum of 4 fields
IWEBUTIL_ParseFormFields(piwu,&pszIter,wff,4,0);
IWEBUTIL_Release(piwu);
piwu = 0;
}
现在,表域的名称和值都包含在WebFormFields中了。