封装IWeb其实并不算是GUI的范畴,但是一并说了罢。在BREW中实现网络访问有两个选择:ISocket和IWeb,我是比较倾向于使用IWeb 的,只要你不是要做一个长连接。因为服务端程序比较容易做,可以直接使用现成的WEB服务器,我们不再需要自己处理并发、缓存等网络服务必须要考虑的东西。
使用IWeb并非只能处理HTML的页面,事实上我们完全可以拿它来做文件下载、提交信息(如下载报告)之类的管理。也并非IWeb 只能与IHtmlViewer合并使用,我们完全可以将一个IMenuCtl的信息打成二进制包使用IWeb下载并用IMenuCtl来展示,也就是说把它当成一个ISocket一样来使用。
首先我们需要定义一个回调,用WSNotify定义了回调的通知参数,包括状态、代码以及获取的内容等等。
typedef
struct
_IWebSocket IWebSocket;
//
定义回调的参数
typedef
struct
...
{
uint16 wStatus;
int wCode;
byte * pBuffer;
uint32 bLength;
uint32 cLength;
}
WSNotify;
//
定义回调格式
typedef
void
(
*
PFNWSOCKETNOTIFY)(
void
*
pvUser,
WSNotify aWSNotify
);
再看看这个组件包括的成员变量有哪些吧,除了IWeb和IWebResp以外,还需要回调cbNotify、目标地址m_TargetURL、临时的内容缓冲区m_BodyBuffer等成员:
struct
_IWebSocket
...
{
const AEEVTBL(IWebSocket) * pvt;
uint32 m_nRefs;
IShell * m_pIShell;
IModule * m_pIModule;
AEECallback cb;
PFNWSOCKETNOTIFY cbNotify;
void * pUser;
IWeb * m_pIWeb;
IWebResp * m_piWResp;
// 保存下载文件内容的缓冲区
byte * m_BodyBuffer;
uint32 m_BodySize;
uint32 m_BodyAllocSize;
char * m_LocationURL;
// 目标URL
char * m_TargetURL;
char * m_Header;
// 当前状态
uint8 m_State;
// 尝试次数
uint8 tryCount;
byte buf[ 1024 ];
byte cBuffer[ 10240 ];
int cLen;
}
;
要实现的接口函数是:
AEEINTERFACE(IWebSocket)
...
{
DECLARE_IBASE(IWebSocket)
byte * ( * GetBuffer) (IWebSocket * po,uint32 * outSize);
void ( * SetNotifyFn) (IWebSocket * po,PFNWSOCKETNOTIFY cb, void * pUser);
void ( * SetHeader) (IWebSocket * po, const char * header);
void ( * SetURL) (IWebSocket * po, const char * url);
void ( * Stop) (IWebSocket * po);
void ( * Resume) (IWebSocket * po);
void ( * Start) (IWebSocket * po);
void ( * StartStream) (IWebSocket * po);
}
;
OK,实现就类似于BREW中的那个 IWeb例子,做两个Start函数,一个是普通的下载(每下一个包回调一次),另一个是流式(建立一个小的缓冲区,这个小缓冲满了则回调一次):
static
void
IWebSocket_Start(IWebSocket
*
pMe)
...
{
FREEIF(pMe -> m_BodyBuffer);
pMe -> m_BodyBuffer = NULL;
pMe -> m_BodySize = 0 ;
pMe -> m_BodyAllocSize = 0 ;
CALLBACK_Cancel( & pMe -> cb);
CALLBACK_Init( & pMe -> cb, webDownloadData, pMe);
if ( ! pMe -> m_pIWeb)
initWeb(pMe);
IWEB_GetResponse(pMe -> m_pIWeb,
(pMe -> m_pIWeb, & pMe -> m_piWResp, & pMe -> cb, pMe -> m_TargetURL,
WEBOPT_HANDLERDATA, pMe,
WEBOPT_HEADERHANDLER, webHeader,
WEBOPT_STATUSHANDLER, webStatus,
WEBOPT_END));
}
static
void
IWebSocket_StartStream(IWebSocket
*
pMe)
...
{
FREEIF(pMe -> m_BodyBuffer);
pMe -> m_BodyBuffer = NULL;
pMe -> m_BodySize = 0 ;
pMe -> m_BodyAllocSize = 0 ;
pMe -> cLen = 0 ;
MEMSET(pMe -> buf, 0 , sizeof (pMe -> buf));
MEMSET(pMe -> cBuffer, 0 , sizeof (pMe -> cBuffer));
CALLBACK_Cancel( & pMe -> cb);
CALLBACK_Init( & pMe -> cb, webDownloadStream, pMe);
if ( ! pMe -> m_pIWeb)
initWeb(pMe);
IWEB_GetResponse(pMe -> m_pIWeb,
(pMe -> m_pIWeb, & pMe -> m_piWResp, & pMe -> cb, pMe -> m_TargetURL,
WEBOPT_HANDLERDATA, pMe,
WEBOPT_HEADERHANDLER, webHeader,
WEBOPT_STATUSHANDLER, webStatus,
WEBOPT_END));
}
两个下载的具体实现是:
//
响应
static
void
webDownloadData(
void
*
p)
...
{
IWebSocket * pMe = (IWebSocket * )p;
WebRespInfo * pwri;
int ByteCount;
pwri = IWEBRESP_GetInfo(pMe -> m_piWResp);
if ( ! WEB_ERROR_SUCCEEDED(pwri -> nCode) )
... {
FREEIF(pMe -> m_BodyBuffer);
pMe -> m_BodyBuffer = NULL;
pMe -> m_BodySize = 0 ;
pMe -> m_BodyAllocSize = 0 ;
if ( (pwri -> nCode == 302 || pwri -> nCode == 301 ) && pMe -> m_LocationURL != NULL )
... {
pMe -> tryCount = 0 ;
FREEIF(pMe -> m_TargetURL);
pMe -> m_TargetURL = NULL;
pMe -> m_TargetURL = STRDUP(pMe -> m_LocationURL);
FREEIF(pMe -> m_LocationURL);
pMe -> m_LocationURL = NULL;
IWebSocket_Stop(pMe);
ISHELL_SetTimer(pMe -> m_pIShell, 0 ,(PFNNOTIFY)IWebSocket_Start,( void * )pMe);
return ;
}
if (pMe -> tryCount < MAX_TRY_COUNT)
... {
IWebSocket_Stop(pMe);
pMe -> tryCount ++ ;
ISHELL_SetTimer(pMe -> m_pIShell, 3000 ,(PFNNOTIFY)IWebSocket_Start,( void * )pMe);
}
else
... {
WSNotify wsn;
wsn.wCode = pwri -> nCode;
wsn.wStatus = WS_STATUS_ERROR;
wsn.pBuffer = NULL;
wsn.bLength = 0 ;
wsn.cLength = 0 ;
pMe -> cbNotify(pMe -> pUser,wsn);
}
return ;
}
ISHELL_CancelTimer(pMe -> m_pIShell,(PFNNOTIFY)IWebSocket_Start,( void * )pMe);
MEMSET(pMe -> buf, 0 , sizeof (pMe -> buf));
if ((ISource * ) 0 != pwri -> pisMessage)
... {
ISource * pISource = pwri -> pisMessage;
ByteCount = ISOURCE_Read(pISource, ( char * )pMe -> buf, sizeof (pMe -> buf));
switch (ByteCount)
... {
case ISOURCE_END: // 表示读网络完成
... {
WSNotify wsn;
IWebSocket_Stop(pMe);
wsn.wCode = 0 ;
wsn.wStatus = WS_STATUS_SUCCESS;
wsn.pBuffer = pMe -> m_BodyBuffer;
wsn.bLength = pMe -> m_BodySize;
wsn.cLength = pwri -> lContentLength;
pMe -> cbNotify(pMe -> pUser,wsn);
break ;
}
case ISOURCE_ERROR:
... {
WSNotify wsn;
wsn.wCode = pwri -> nCode;
wsn.wStatus = WS_STATUS_ERROR;
wsn.pBuffer = NULL;
wsn.bLength = 0 ;
wsn.cLength = pwri -> lContentLength;
pMe -> cbNotify(pMe -> pUser,wsn);
}
break ;
case ISOURCE_WAIT:
ISOURCE_Readable(pISource, & pMe -> cb);
break ;
default :
if (ByteCount)
... {
if (pMe -> m_BodySize + ByteCount > pMe -> m_BodyAllocSize)
... {
const int NewSize = pMe -> m_BodyAllocSize + ByteCount;
byte * NewBuf = ( byte * )REALLOC(pMe -> m_BodyBuffer, NewSize);
if (NewBuf)
... {
pMe -> m_BodyBuffer = NewBuf;
pMe -> m_BodyAllocSize = NewSize;
}
else
return ;
}
if (pMe -> m_BodySize + ByteCount <= pMe -> m_BodyAllocSize)
... {
MEMCPY(pMe -> m_BodyBuffer + pMe -> m_BodySize, pMe -> buf, ByteCount);
pMe -> m_BodySize += ByteCount;
}
// 每次下一个包,则回调一次
... {
WSNotify wsn;
wsn.wCode = pwri -> nCode;
wsn.wStatus = WS_STATUS_TICK;
wsn.pBuffer = pMe -> m_BodyBuffer;
wsn.bLength = pMe -> m_BodySize;
wsn.cLength = pwri -> lContentLength;
pMe -> cbNotify(pMe -> pUser,wsn);
}
}
ISOURCE_Readable(pISource, & pMe -> cb);
break ;
}
}
}
和流式:
//
流式响应
static
void
webDownloadStream(
void
*
p)
...
{
IWebSocket * pMe = (IWebSocket * )p;
WebRespInfo * pwri;
int ByteCount;
pwri = IWEBRESP_GetInfo(pMe -> m_piWResp);
if ( ! WEB_ERROR_SUCCEEDED(pwri -> nCode) )
... {
FREEIF(pMe -> m_BodyBuffer);
pMe -> m_BodyBuffer = NULL;
pMe -> m_BodySize = 0 ;
pMe -> m_BodyAllocSize = 0 ;
if ( (pwri -> nCode == 302 || pwri -> nCode == 301 ) && pMe -> m_LocationURL != NULL )
... {
pMe -> tryCount = 0 ;
FREEIF(pMe -> m_TargetURL);
pMe -> m_TargetURL = NULL;
pMe -> m_TargetURL = STRDUP(pMe -> m_LocationURL);
FREEIF(pMe -> m_LocationURL);
pMe -> m_LocationURL = NULL;
IWebSocket_Stop(pMe);
ISHELL_SetTimer(pMe -> m_pIShell, 0 ,(PFNNOTIFY)IWebSocket_StartStream,( void * )pMe);
return ;
}
if (pMe -> tryCount < MAX_TRY_COUNT)
... {
IWebSocket_Stop(pMe);
pMe -> tryCount ++ ;
ISHELL_SetTimer(pMe -> m_pIShell, 3000 ,(PFNNOTIFY)IWebSocket_StartStream,( void * )pMe);
}
else
... {
WSNotify wsn;
wsn.wCode = pwri -> nCode;
wsn.wStatus = WS_STATUS_ERROR;
wsn.pBuffer = NULL;
wsn.cLength = 0 ;
wsn.bLength = 0 ;
pMe -> cbNotify(pMe -> pUser,wsn);
}
return ;
}
ISHELL_CancelTimer(pMe -> m_pIShell,(PFNNOTIFY)IWebSocket_StartStream,( void * )pMe);
MEMSET(pMe -> buf,( byte ) ' ' , sizeof (pMe -> buf));
if ((ISource * ) 0 != pwri -> pisMessage)
... {
ISource * pISource = pwri -> pisMessage;
ByteCount = ISOURCE_Read(pISource, ( char * )pMe -> buf, sizeof (pMe -> buf));
switch (ByteCount)
... {
case ISOURCE_END: // 表示读网络完成
... {
WSNotify wsn;
IWebSocket_Stop(pMe);
// 先回调一次tick
wsn.wCode = pwri -> nCode;
wsn.wStatus = WS_STATUS_TICK;
wsn.pBuffer = pMe -> cBuffer;
wsn.bLength = pMe -> cLen;
wsn.cLength = pwri -> lContentLength;
pMe -> cbNotify(pMe -> pUser,wsn);
// 再回调成功
wsn.wCode = 0 ;
wsn.wStatus = WS_STATUS_SUCCESS;
wsn.pBuffer = NULL;
wsn.bLength = 0 ;
wsn.cLength = pwri -> lContentLength;
pMe -> cbNotify(pMe -> pUser,wsn);
break ;
}
case ISOURCE_ERROR:
... {
WSNotify wsn;
wsn.wCode = pwri -> nCode;
wsn.wStatus = WS_STATUS_ERROR;
wsn.pBuffer = NULL;
wsn.bLength = 0 ;
wsn.cLength = pwri -> lContentLength;
pMe -> cbNotify(pMe -> pUser,wsn);
}
break ;
case ISOURCE_WAIT:
ISOURCE_Readable(pISource, & pMe -> cb);
break ;
default :
if (ByteCount)
... {
if (pMe -> cLen + ByteCount <= sizeof (pMe -> cBuffer))
... {
// 如果可以先放入临时空间
MEMCPY(pMe -> cBuffer + pMe -> cLen, pMe -> buf, ByteCount);
pMe -> cLen += ByteCount;
}
else
... {
// 临时空间如果满了,则回调一次
WSNotify wsn;
wsn.wCode = pwri -> nCode;
wsn.wStatus = WS_STATUS_TICK;
wsn.pBuffer = pMe -> cBuffer;
wsn.bLength = pMe -> cLen;
wsn.cLength = pwri -> lContentLength;
pMe -> cbNotify(pMe -> pUser,wsn);
// 然后将当前的内容再放入临时空间,从头开始了
MEMCPY(pMe -> cBuffer,pMe -> buf,ByteCount);
pMe -> cLen = ByteCount;
}
}
ISOURCE_Readable(pISource, & pMe -> cb);
break ;
}
}
}
OK。就是这样了,可以在回调中使用那个pBuffer,也可以在最后用GetBuffer取结果。