版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.06.09 |
前言
CFNetwork框架访问网络服务并处理网络配置的变化。 建立在网络协议抽象的基础上,可以简化诸如使用BSD套接字,管理HTTP和FTP服务器以及管理Bonjour服务等任务。接下来几篇我们就一起看一下这个框架。感兴趣的可以看上面几篇文章。
1. CFNetwork框架详细解析(一) —— 基本概览
2. CFNetwork框架详细解析(二) —— CFNetwork编程指导之简介(一)
3. CFNetwork框架详细解析(三) —— CFNetwork编程指导之CFNetwork概念(二)
4. CFNetwork框架详细解析(四) —— CFNetwork编程指导之流的处理(三)
Communicating with HTTP Servers - 与HTTP服务器通信
本章介绍如何创建,发送和接收HTTP请求和响应。
Creating a CFHTTP Request - 创建一个CFHTTP请求
HTTP请求是由远程服务器执行的方法,对其进行操作的对象(URL),消息标题message headers
和消息正文message body
组成的消息。 这些方法通常是以下其中一种:GET,HEAD,PUT,POST,DELETE,TRACE,CONNECT或OPTIONS
。 使用CFHTTP
创建HTTP
请求需要四个步骤:
- 使用
CFHTTPMessageCreateRequest
函数生成一个CFHTTP
消息对象。 - 使用函数
CFHTTPMessageSetBody
设置消息的正文。 - 使用
CFHTTPMessageSetHeaderFieldValue
函数设置消息的标题。 - 通过调用函数
CFHTTPMessageCopySerializedMessage
来序列化消息。
示例代码如Listing 3-1
中所示。
Listing 3-1 Creating an HTTP request
CFStringRef bodyString = CFSTR(""); // Usually used for POST data
CFDataRef bodyData = CFStringCreateExternalRepresentation(kCFAllocatorDefault,
bodyString, kCFStringEncodingUTF8, 0);
CFStringRef headerFieldName = CFSTR("X-My-Favorite-Field");
CFStringRef headerFieldValue = CFSTR("Dreams");
CFStringRef url = CFSTR("http://www.apple.com");
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url, NULL);
CFStringRef requestMethod = CFSTR("GET");
CFHTTPMessageRef myRequest =
CFHTTPMessageCreateRequest(kCFAllocatorDefault, requestMethod, myURL,
kCFHTTPVersion1_1);
CFDataRef bodyDataExt = CFStringCreateExternalRepresentation(kCFAllocatorDefault, bodyData, kCFStringEncodingUTF8, 0);
CFHTTPMessageSetBody(myRequest, bodyDataExt);
CFHTTPMessageSetHeaderFieldValue(myRequest, headerFieldName, headerFieldValue);
CFDataRef mySerializedRequest = CFHTTPMessageCopySerializedMessage(myRequest);
在这个示例代码中,首先通过调用CFURLCreateWithString
将url转换为CFURL
对象。然后使用四个参数调用CFHTTPMessageCreateRequest
:kCFAllocatorDefault
指定默认的系统内存分配器用于创建消息引用,requestMethod
指定方法,例如POST方法,myURL
指定URL,例如http:// www.apple.com
和kCFHTTPVersion1_1
指定该消息的HTTP版本为1.1。
然后,由CFHTTPMessageCreateRequest
返回的消息对象引用(myRequest)
随同消息体(bodyData)
一起被发送到CFHTTPMessageSetBody
。然后调用CFHTTPMessageSetHeaderFieldValue
,使用相同的消息对象引用以及头的名称(headerField)
和要设置的值(value)
。 header
参数是一个CFString
对象,比如Content-Length
,而value参数是一个CFString对象,如1260.
最后,通过调用CFHTTPMessageCopySerializedMessage
对消息进行序列化,并且应该通过写入流发送给预期的接收者,在这个例子中http://www.apple.com
。
注意:请求体通常被省略。 使用请求主体的主要位置在POST请求中以包含POST数据。 它也可以用于与HTTP扩展相关的其他一些请求类型,例如
WebDAV
。 有关更多信息,请参阅RFC 2616。
当消息不再需要时,释放消息对象和序列化消息。 有关示例代码,请参见Listing 3-2
。
Listing 3-2 Releasing an HTTP request
CFRelease(myRequest);
CFRelease(myURL);
CFRelease(url);
CFRelease(mySerializedRequest);
myRequest = NULL;
mySerializedRequest = NULL;
Creating a CFHTTP Response - 创建一个CFHTTP响应
创建HTTP响应的步骤几乎与创建HTTP请求的步骤相同。 唯一的区别是不是调用CFHTTPMessageCreateRequest
,而是使用相同的参数调用函数CFHTTPMessageCreateResponse
。
Deserializing an Incoming HTTP Request - 反序列化传入的HTTP请求
要反序列化传入的HTTP请求,请使用CFHTTPMessageCreateEmpty
函数创建一条空消息,并以isRequest
参数的形式传递TRUE
以指定将创建一个空请求消息。 然后使用函数CFHTTPMessageAppendBytes
将传入消息附加到空消息。 CFHTTPMessageAppendBytes
将消息反序列化并移除它可能包含的任何控制信息。
继续执行此操作,直到函数CFHTTPMessageIsHeaderComplete
返回TRUE
。 如果您没有检查CFHTTPMessageIsHeaderComplete
是否返回TRUE,则该消息可能不完整且不可靠。 在Listing 3-3
中可以看到使用这两个函数的示例。
Listing 3-3 Deserializing a message
CFHTTPMessageRef myMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE);
if (!CFHTTPMessageAppendBytes(myMessage, &data, numBytes)) {
//Handle parsing error
}
在该示例中,data
是要附加的数据,numBytes
是data的长度。 您可能需要调用CFHTTPMessageIsHeaderComplete
来验证附加消息的标题是否完整。
if (CFHTTPMessageIsHeaderComplete(myMessage)) {
// Perform processing.
}
通过反序列化消息,您现在可以调用以下任何函数从消息中提取信息:
-
CFHTTPMessageCopyBody
获取消息正文的副本 -
CFHTTPMessageCopyHeaderFieldValue
获取特定标题字段值的副本 -
CFHTTPMessageCopyAllHeaderFields
获得所有消息标题字段的副本 -
CFHTTPMessageCopyRequestURL
获取消息URL的副本 -
CFHTTPMessageCopyRequestMethod
获取消息请求方法的副本
当你不再需要信息时,正确地释放和处理它。
Deserializing an Incoming HTTP Response - 反序列化传入的HTTP响应
正如创建HTTP请求与创建HTTP响应非常类似,反序列化传入的HTTP请求也非常类似于反序列化传入的HTTP响应。 唯一重要的区别是当调用CFHTTPMessageCreateEmpty
时,必须将FALSE
传递给isRequest
参数,以指定要创建的消息是响应消息。
Using a Read Stream to Serialize and Send HTTP Requests - 使用读取流来序列化和发送HTTP请求
您可以使用CFReadStream
对象序列化并发送CFHTTP
请求。 当您使用CFReadStream
对象发送CFHTTP
请求时,打开流会导致消息被序列化并一步发送。 使用CFReadStream
对象发送CFHTTP请求可以很容易地获得对请求的响应,因为响应可用作流的属性。
1. Serializing and Sending an HTTP Request - 序列化和发送HTTP请求
要使用CFReadStream
对象序列化并发送HTTP请求,请首先按照Creating a CFHTTP Request中的描述创建CFHTTP请求并设置消息正文和标头。 然后通过调用函数CFReadStreamCreateForHTTPRequest
并传递刚刚创建的请求来创建一个CFReadStream
对象。 最后,用CFReadStreamOpen
打开读取流。
当调用CFReadStreamCreateForHTTPRequest
时,它会复制它传递的CFHTTP
请求对象。 因此,如果需要,可以在调用CFReadStreamCreateForHTTPRequest之后立即释放CFHTTP请求对象。
由于在创建CFHTTP请求时,读取流将与myUrl参数指定的服务器建立套接字连接,因此在认为该流打开之前必须允许经过一段时间。 打开读取流也会导致请求被序列化并发送。
Listing 3-4
中可以看到如何序列化和发送HTTP请求的示例。
Listing 3-4 Serializing an HTTP request with a read stream
CFStringRef url = CFSTR("http://www.apple.com");
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url, NULL);
CFStringRef requestMethod = CFSTR("GET");
CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault,
requestMethod, myUrl, kCFHTTPVersion1_1);
CFHTTPMessageSetBody(myRequest, bodyData);
CFHTTPMessageSetHeaderFieldValue(myRequest, headerField, value);
CFReadStreamRef myReadStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest);
CFReadStreamOpen(myReadStream);
2. Checking the Response - 检查响应
在运行循环中调度请求之后,您最终将获得标题完成回调。 此时,您可以调用CFReadStreamCopyProperty从读取流获取消息响应。
CFHTTPMessageRef myResponse = (CFHTTPMessageRef)CFReadStreamCopyProperty(myReadStream, kCFStreamPropertyHTTPResponseHeader);
您可以通过调用函数CFHTTPMessageCopyResponseStatusLine
从响应消息中获取完整的状态行:
CFStringRef myStatusLine = CFHTTPMessageCopyResponseStatusLine(myResponse);
或者通过调用函数CFHTTPMessageGetResponseStatusCode
从响应消息中获取状态码:
UInt32 myErrCode = CFHTTPMessageGetResponseStatusCode(myResponse);
注意:如果您正在同步使用此类(无需在运行循环中调度它),则必须在调用
CFReadStreamCopyProperty
之前至少调用一次CFReadStreamRead来读取消息。CFReadStreamRead
调用阻塞,直到数据可用(或连接失败)。 不要在主应用程序线程上执行此操作。
Handling Authentication Errors - 处理认证错误
如果函数CFHTTPMessageGetResponseStatusCode
返回的状态代码是401
(远程服务器需要认证信息)或407
(代理服务器需要认证),则需要将认证信息附加到请求并再次发送。 请阅读Communicating with Authenticating HTTP Servers以获取有关如何处理身份验证的信息。
Handling Redirection Errors - 处理重定向错误
当CFReadStreamCreateForHTTPRequest
创建读取流时,默认情况下禁用流的自动重定向。 如果发送请求的统一资源定位符或URL被重定向到另一个URL,则发送该请求将导致错误,其状态码范围为300
到307
,如果收到重定向错误,则需要关闭流,再次创建流,为其启用自动重定向,并打开流。 参见Listing 3-5
。
Listing 3-5 Redirecting an HTTP stream
CFReadStreamClose(myReadStream);
CFReadStreamRef myReadStream =
CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest);
if (CFReadStreamSetProperty(myReadStream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue) == false) {
// something went wrong, exit
}
CFReadStreamOpen(myReadStream);
您可能希望在创建读取流时启用自动重定向。
Canceling a Pending Request - 取消待处理的请求
一旦请求发送完毕,就无法阻止远程服务器对其执行操作。 但是,如果您不再关心响应数据,则可以关闭流。
重要提示:当另一个线程正在等待来自该流的内容时,不要关闭任何线程中的流。 如果您需要能够终止请求,则应使用非阻塞I / O,如Preventing Blocking When Working with Streams中所述。 确保在关闭它之前从运行循环中移除流。
后记
本篇主要讲述了与HTTP服务器通信,感兴趣的给个赞或者关注~~~~