作者:孫東風 2009-12-01(轉載請註明出處)
在《自己動手寫iPhone wap瀏覽器之預備篇》中筆者講述了進行iPhone wap瀏覽器開發的主要流程如下:
² 封裝BSD Socket進行HTTP請求。
² 將請求到的WML頁面解析成XML資料結構。
² 渲染需要在介面上顯示的WML標籤(英文名tag)。
² 將渲染後的WML標籤顯示在介面上(UIView)。
在《html" target=_blank>自己動手寫iPhone wap瀏覽器之預備篇》中已經講述了利用tinyxml解析請求到的XML頁面內容的知識,這個章節裡主要講述利用BSD Socket封裝HTTP引擎的知識。在筆者的文章《玩轉iPhone網路通訊之BSD Socket篇》中已經初步講解了iPhone中利用BSD Socket進行網路通訊的關鍵技術點,但是筆者只是把請求的WML頁面內容儲存在一個緩衝區內。在實際應用中,大多數情況下需要解析請求到的WML頁面內容從而區分開HTTP響應的包頭、包體,有時候還需要解析HTTP包頭的每行內容。要做到這些,首先需要BSD Socket引擎同步的解析請求到的資料,修改部分如下:
NSMutableString* readString = [[NSMutableString alloc] init];
char readBuffer[1];
int br = 0;
NSMutableString* readHeaderBufferStr = [[NSMutableString alloc] init];
while((br = recv(sockfd, readBuffer, sizeof(readBuffer), 0)))
{
[readHeaderBufferStr appendString:[NSString stringWithCString:readBuffer length:sizeof(readBuffer)]];
if([self RecvRespHeaderFinished:readHeaderBufferStr])
{
break;
}else
{
}
}
筆者把緩衝區的大小改為1,這樣每次讀取一個字元到緩衝內並把每次讀取到的內容添加到readHeaderBufferStr內,之後調用RecvRespHeaderFinished:readHeaderBufferStr方法判斷HTTP頭部內容是否讀取完成,這個方法的實現如下:
- (BOOL)RecvRespHeaderFinished:(NSString*) aReadBuffer
{
int len = [aReadBuffer length];
if(len < 12)
{
return NO;
}
if([aReadBuffer characterAtIndex:(len-4)] == (const unichar)&&
[aReadBuffer characterAtIndex:(len-3)] == (const unichar)&&
[aReadBuffer characterAtIndex:(len-2)] == (const unichar)&&
[aReadBuffer characterAtIndex:(len-1)] == (const unichar))
{
NSLog(@"ResponseHeader = %@",aReadBuffer);
int nCode = [self GetResponseCode:aReadBuffer];
NSLog(@"get http response code = %d",nCode);
if(nCode > 299 || nCode < 200)
{
NSLog(@"ErrMsg:Server response code is %d.",nCode);
close(sockfd);
}
contentlen = [[self GetHttpHdrFieldValue:aReadBuffer aField:EContentLength] intValue];
NSLog(@"contentlen = %d",contentlen);
return YES;
}
return NO;
}
筆者通過判斷緩衝字串的後四個字元是否依次為’’、’’、’’、’’來斷定HTTP頭部是否解析完成,如果解析完成則列印出來並返回YES,否則返回NO,最後並調用GetHttpHdrFieldValue:aReadBuffer:aField方法擷取HTTP包頭中指定行的value值,在這裡筆者需要擷取"Content-Length"的value值以便知道HTTP包體的長度,列印結果如下:
9-12-01 20:39:03.337 BSDHttpExample[253:207] getIpAddressForHost :220.181.37.183
2009-12-01 20:39:03.404 BSDHttpExample[253:207] Connect errno is :0
2009-12-01 20:39:03.404 BSDHttpExample[253:207] Then the conn is not -1!
2009-12-01 20:39:03.405 BSDHttpExample[253:207] httpCotent is :GET / HTTP/1.1
Host:wap.baidu.com
2009-12-01 20:39:03.406 BSDHttpExample[253:207] Sended content is :GET / HTTP/1.1
Host:wap.baidu.com
2009-12-01 20:39:03.406 BSDHttpExample[253:207] Datas have been sended over!
send 38 bytes to 220.181.37.183
2009-12-01 20:39:03.501 BSDHttpExample[253:207] ResponseHeader = HTTP/1.1 200 OK
Date: Tue, 01 Dec 2009 12:39:03 GMT
Server: Apache
Content-Length: 4638
Content-Type: text/vnd.wap.wml;charset=utf-8
Age: 0
Cache-Control: no-cache
Expires: -1
Set-Cookie: BAIDU_WISE_UID=frontui_1259671143_7379; Max-Age=800000000; expires=Sun, 08-Apr-35 18:52:23 GMT; path=/; domain=.baidu.com;
Vary: Accept-Encoding,User-Agent
Connection: close
可見,通過上面的方法成功的解析出來HTTP響應的頭部內容並擷取到HTTP包體的長度,那麼接下來就需要解析HTTP包體的內容了,代碼如下:
NSMutableString* readBodyBufferStr = [[NSMutableString alloc] init];
while((br = recv(sockfd, readBuffer, sizeof(readBuffer), 0)))
{
if(recLen < contentlen)
{
recLen++;
[readBodyBufferStr appendString:[NSString stringWithCString:readBuffer length:sizeof(readBuffer)]];
}else
{
}
}
NSLog(@"hava received all data = %@",readBodyBufferStr);
通過判斷包體緩衝字串的長度和“Content-Length”的值來決定HTTP包體內容是否已經解析完成,列印結果如下:
2009-12-01 20:39:03.503 BSDHttpExample[253:207] contentlen = 4638
2009-12-01 20:39:03.532 BSDHttpExample[253:207] hava received all data =
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "