自己動手寫iPhone wap瀏覽器之BSD Socket引擎篇(手把手教你iphone開發 – 進階篇)
作者:孫東風 2009-12-01(轉載請註明出處)
在《自己動手寫iPhone wap瀏覽器之預備篇》中筆者講述了進行iPhone wap瀏覽器開發的主要流程如下:
² 封裝BSD Socket進行HTTP請求。
² 將請求到的WML頁面解析成XML數據結構。
² 渲染需要在界面上顯示的WML標籤(英文名tag)。
² 將渲染後的WML標籤顯示在界面上(UIView)。
在《自己動手寫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)'/r'&&
[aReadBuffer characterAtIndex:(len-3)] == (const unichar)'/n'&&
[aReadBuffer characterAtIndex:(len-2)] == (const unichar)'/r'&&
[aReadBuffer characterAtIndex:(len-1)] == (const unichar)'/n')
{
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;
}
筆者通過判斷緩衝字符串的後四個字符是否依次爲’/r’、’/n’、’/r’、’/n’來斷定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 = /n%@",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" "http://www.wapforum.org/DTD/wml_1.1.xml"><wml><!--STATUS OK--><card title="□□□□∫□‰∏□‰∏□,‰Ω□□∞±□ü□□□ì"><p><img src="/r/wise/wapsearchindex/logoindexsmall.gif" alt="□□□□∫□□□□□°□" /><br/><input name="word" emptyok="true"/><br/><anchor>□êú□Ω□□°□<go href="/s" method="get"><postfield name="tn" value="webmain"/><postfield name="word" value="$(word)"/><postfield name="ssid" value="0"/><postfield name="from" value="0"/><postfield name="vit" value=""/><postfield name="bd_page_type" value="0"/><postfield name="uid" value="frontui_1259671143_7379"/><postfield name="st" value="111041"/><postfield name="pu" value="pd@1,uc@0"/><postfield name="rn" value="10"/><postfield name="pn" value="0"/></go></anchor> <anchor>□êúWap<go href="/s" method="get"><postfield name="tn" value="fwapadv"/><postfield name="word" value="$(word)"/><postfield name="ssid" value="0"/><postfield name="from" value="0"/><postfield name="bd_page_type" value="0"/><postfield name="uid" value="frontui_1259671143_7379"/><postfield name="vit" value=""/><postfield name="st" value="102041"/><postfield name="pu" value="pd@1,uc@0"/><postfield name="rn" value="10"/><postfield name="pn" value="0"/></go></anchor><br/>□ú□<a href="http://wap.baidu.com/news?tn=bdwcn&statcms=index_jrjd&word=todaynews&rn=10&pn=0&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□□□ó□</a>|<a href="/fengyun/fengyun_novel_1.jsp?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0&statcms=index_novel">□∞è□□□</a>|<a href="http://m.baidu.com/fengyun/jingmeibizhi.jsp?ssid=0&from=0&bd_page_type=1&uid=uc_MTI1ODU5OTc0Mzo0Njc1NzYw_743&pu=pd@1,uc@2&statcms=img_jingmeibizhi">□□é□□□</a>|<a href="http://wap.baidu.com/fengyun/fengyun_fast_1.jsp?stat=novel_fast&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□≠□□ú</a><br/>□é□<a href="/fengyun/fengyun_game_index.jsp?statcms=index_game&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□□□ú∫□∏∏□àè</a>|<a href="http://wap.skycn.com/?statcms=soft&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□□□§á□Ω□‰□□</a><br/><br/><a href="/img?stat=img&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□□□□á</a>|<a href="http://wapp.baidu.com/?stat=tieba&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□□□ê□</a>|<a href="http://wapiknow.baidu.com/?stat=iknow&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□ü□□□ì</a>|<a href="http://waphi.baidu.com/?stat=waphi&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□∫□ó□</a><br/><a href="/news?stat=news&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□∞□ó□</a>|<a href="/dt/index.jsp?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□ú∞□□□</a>|<a href="/tq?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□§□□∞□</a>|<a href="/s?tn=wisedict&mark=3&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□□□□∏</a><br/><a href="/s?tn=wisestock&mark=5&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□°□□□</a>|<a href="/s?tn=wisetraffic&mark=4&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□àó□Ω□□à□□è≠</a>|<a href="/more.jsp?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0">□□□□§□</a><br/><br/><a href="http://mo.baidu.com/index1.wml">□□□□□π:□é□‰∏□□□□□∫□|□□□□∫□□□□□ú∫□□ì□□□□≥□</a><br/><a href="http://wap.hao123.com/index.wml?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0&vit=&tt=G1F11">hao123□Ω□□ù□‰π□□□□</a><br/><br/><a href="/wxlm.jsp?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0&vit=&tt=H1E11">□ó□□∫□□êú□□□□□□□□ü</a><br/><a href="/help/help.jsp?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0&vit=&tt=I1211">□∏□□□□</a>|<a href="http://wapp.baidu.com/f?kw=%B0%D9%B6%C8%CA%D6%BB%FA%CB%D1%CB%F7%B0%EF%D6%FA&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0&vit=&tt=78811">□è□□□à</a><br/>2009-12-1 20:39</p></card></wml>
可見,接收到了一個完整的WML頁面內容,而這個WML頁面的內容是完全符合XML的基本格式規範的,所以下面可以通過tinyxml解析這個請求到的HTTP包體,從而解析出來需要渲染的WML標籤,爲下一章節的頁面渲染作準備,代碼如下:
NSLog(@"**********Now start parsing xml data**********/n");
XMLParserEx *xmlParser = XMLParserEx::GetInstance();
xmlParser->parsexml([readBodyBufferStr UTF8String]);
close(sockfd);
[readBodyBufferStr release];
[readString release];
筆者調用《自己動手寫iPhone wap瀏覽器之預備篇》中寫好的parsexml(const char* buffer)方法解析請求到的HTTP包體內容,打印結果如下:
parse xml succeed
aChild value = STATUS OK
aChild value = card
attr name = title, attr value = □□□□∫□‰∏□‰∏□,‰Ω□□∞±□ü□□□ì
aChild value = p
aChild value = img
attr name = src, attr value = /r/wise/wapsearchindex/logoindexsmall.gif
attr name = alt, attr value = □□□□∫□□□□□°□
aChild value = br
aChild value = input
attr name = name, attr value = word
attr name = emptyok, attr value = true
aChild value = br
aChild value = anchor
aChild value = □êú□Ω□□°□
aChild Value = □êú□Ω□□°□
aChild value = go
attr name = href, attr value = /s
attr name = method, attr value = get
aChild value = postfield
attr name = name, attr value = tn
attr name = value, attr value = webmain
aChild value = postfield
attr name = name, attr value = word
attr name = value, attr value = $(word)
aChild value = postfield
attr name = name, attr value = ssid
attr name = value, attr value = 0
aChild value = postfield
attr name = name, attr value = from
attr name = value, attr value = 0
aChild value = postfield
attr name = name, attr value = vit
attr name = value, attr value =
aChild value = postfield
attr name = name, attr value = bd_page_type
attr name = value, attr value = 0
aChild value = postfield
attr name = name, attr value = uid
attr name = value, attr value = frontui_1259671143_7379
aChild value = postfield
attr name = name, attr value = st
attr name = value, attr value = 111041
aChild value = postfield
attr name = name, attr value = pu
attr name = value, attr value = pd@1,uc@0
aChild value = postfield
attr name = name, attr value = rn
attr name = value, attr value = 10
aChild value = postfield
attr name = name, attr value = pn
attr name = value, attr value = 0
aChild value = nbsp;
aChild Value = nbsp;
aChild value = anchor
aChild value = □êúWap
aChild Value = □êúWap
aChild value = go
attr name = href, attr value = /s
attr name = method, attr value = get
aChild value = postfield
attr name = name, attr value = tn
attr name = value, attr value = fwapadv
aChild value = postfield
attr name = name, attr value = word
attr name = value, attr value = $(word)
aChild value = postfield
attr name = name, attr value = ssid
attr name = value, attr value = 0
aChild value = postfield
attr name = name, attr value = from
attr name = value, attr value = 0
aChild value = postfield
attr name = name, attr value = bd_page_type
attr name = value, attr value = 0
aChild value = postfield
attr name = name, attr value = uid
attr name = value, attr value = frontui_1259671143_7379
aChild value = postfield
attr name = name, attr value = vit
attr name = value, attr value =
aChild value = postfield
attr name = name, attr value = st
attr name = value, attr value = 102041
aChild value = postfield
attr name = name, attr value = pu
attr name = value, attr value = pd@1,uc@0
aChild value = postfield
attr name = name, attr value = rn
attr name = value, attr value = 10
aChild value = postfield
attr name = name, attr value = pn
attr name = value, attr value = 0
aChild value = br
aChild value = □ú□
aChild Value = □ú□
aChild value = a
attr name = href, attr value = http://wap.baidu.com/news?tn=bdwcn&statcms=index_jrjd&word=todaynews&rn=10&pn=0&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□□□ó□
aChild Value = □□□□ó□
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = /fengyun/fengyun_novel_1.jsp?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0&statcms=index_novel
aChild value = □∞è□□□
aChild Value = □∞è□□□
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = http://m.baidu.com/fengyun/jingmeibizhi.jsp?ssid=0&from=0&bd_page_type=1&uid=uc_MTI1ODU5OTc0Mzo0Njc1NzYw_743&pu=pd@1,uc@2&statcms=img_jingmeibizhi
aChild value = □□é□□□
aChild Value = □□é□□□
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = http://wap.baidu.com/fengyun/fengyun_fast_1.jsp?stat=novel_fast&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□≠□□ú
aChild Value = □□≠□□ú
aChild value = br
aChild value = □é□
aChild Value = □é□
aChild value = a
attr name = href, attr value = /fengyun/fengyun_game_index.jsp?statcms=index_game&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□□□ú∫□∏∏□àè
aChild Value = □□□□ú∫□∏∏□àè
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = http://wap.skycn.com/?statcms=soft&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□□□§á□Ω□‰□□
aChild Value = □□□□§á□Ω□‰□□
aChild value = br
aChild value = br
aChild value = a
attr name = href, attr value = /img?stat=img&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□□□□á
aChild Value = □□□□□á
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = http://wapp.baidu.com/?stat=tieba&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□□□ê□
aChild Value = □□□□ê□
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = http://wapiknow.baidu.com/?stat=iknow&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □ü□□□ì
aChild Value = □ü□□□ì
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = http://waphi.baidu.com/?stat=waphi&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□∫□ó□
aChild Value = □□∫□ó□
aChild value = br
aChild value = a
attr name = href, attr value = /news?stat=news&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□∞□ó□
aChild Value = □□∞□ó□
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = /dt/index.jsp?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □ú∞□□□
aChild Value = □ú∞□□□
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = /tq?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □§□□∞□
aChild Value = □§□□∞□
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = /s?tn=wisedict&mark=3&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□□□□∏
aChild Value = □□□□□∏
aChild value = br
aChild value = a
attr name = href, attr value = /s?tn=wisestock&mark=5&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□°□□□
aChild Value = □□°□□□
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = /s?tn=wisetraffic&mark=4&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □àó□Ω□□à□□è≠
aChild Value = □àó□Ω□□à□□è≠
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = /more.jsp?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0
aChild value = □□□□§□
aChild Value = □□□□§□
aChild value = br
aChild value = br
aChild value = a
attr name = href, attr value = http://mo.baidu.com/index1.wml
aChild value = □□□□□π:□é□‰∏□□□□□∫□|□□□□∫□□□□□ú∫□□ì□□□□≥□
aChild Value = □□□□□π:□é□‰∏□□□□□∫□|□□□□∫□□□□□ú∫□□ì□□□□≥□
aChild value = br
aChild value = a
attr name = href, attr value = http://wap.hao123.com/index.wml?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0&vit=&tt=G1F11
aChild value = hao123□Ω□□ù□‰π□□□□
aChild Value = hao123□Ω□□ù□‰π□□□□
aChild value = br
aChild value = br
aChild value = a
attr name = href, attr value = /wxlm.jsp?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0&vit=&tt=H1E11
aChild value = □ó□□∫□□êú□□□□□□□□ü
aChild Value = □ó□□∫□□êú□□□□□□□□ü
aChild value = br
aChild value = a
attr name = href, attr value = /help/help.jsp?ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0&vit=&tt=I1211
aChild value = □∏□□□□
aChild Value = □∏□□□□
aChild value = |
aChild Value = |
aChild value = a
attr name = href, attr value = http://wapp.baidu.com/f?kw=%B0%D9%B6%C8%CA%D6%BB%FA%CB%D1%CB%F7%B0%EF%D6%FA&ssid=0&from=0&bd_page_type=0&uid=frontui_1259671143_7379&pu=pd@1,uc@0&vit=&tt=78811
aChild value = □è□□□à
aChild Value = □è□□□à
aChild value = br
aChild value = 2009-12-1 20:39
可見,tinyxml解析出了WML頁面的全部標籤,在實際的瀏覽器開發中需要把這些標籤分類保存起來以供界面渲染使用,這些內容將來下面的章節中講解。