自己动手写iPhone wap浏览器之BSD Socket引擎篇(手把手教你iphone开发 – 进阶篇)
作者:孙东风 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" "