版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.06.09 |
前言
CFNetwork框架訪問(wèn)網(wǎng)絡(luò)服務(wù)并處理網(wǎng)絡(luò)配置的變化专普。 建立在網(wǎng)絡(luò)協(xié)議抽象的基礎(chǔ)上悯衬,可以簡(jiǎn)化諸如使用BSD套接字,管理HTTP和FTP服務(wù)器以及管理Bonjour服務(wù)等任務(wù)脆诉。接下來(lái)幾篇我們就一起看一下這個(gè)框架甚亭。感興趣的可以看上面幾篇文章。
1. CFNetwork框架詳細(xì)解析(一) —— 基本概覽
2. CFNetwork框架詳細(xì)解析(二) —— CFNetwork編程指導(dǎo)之簡(jiǎn)介(一)
3. CFNetwork框架詳細(xì)解析(三) —— CFNetwork編程指導(dǎo)之CFNetwork概念(二)
4. CFNetwork框架詳細(xì)解析(四) —— CFNetwork編程指導(dǎo)之流的處理(三)
Communicating with HTTP Servers - 與HTTP服務(wù)器通信
本章介紹如何創(chuàng)建击胜,發(fā)送和接收HTTP請(qǐng)求和響應(yīng)亏狰。
Creating a CFHTTP Request - 創(chuàng)建一個(gè)CFHTTP請(qǐng)求
HTTP請(qǐng)求是由遠(yuǎn)程服務(wù)器執(zhí)行的方法,對(duì)其進(jìn)行操作的對(duì)象(URL)偶摔,消息標(biāo)題message headers
和消息正文message body
組成的消息暇唾。 這些方法通常是以下其中一種:GET,HEAD辰斋,PUT策州,POST,DELETE宫仗,TRACE够挂,CONNECT或OPTIONS
。 使用CFHTTP
創(chuàng)建HTTP
請(qǐng)求需要四個(gè)步驟:
- 使用
CFHTTPMessageCreateRequest
函數(shù)生成一個(gè)CFHTTP
消息對(duì)象藕夫。 - 使用函數(shù)
CFHTTPMessageSetBody
設(shè)置消息的正文孽糖。 - 使用
CFHTTPMessageSetHeaderFieldValue
函數(shù)設(shè)置消息的標(biāo)題。 - 通過(guò)調(diào)用函數(shù)
CFHTTPMessageCopySerializedMessage
來(lái)序列化消息毅贮。
示例代碼如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);
在這個(gè)示例代碼中,首先通過(guò)調(diào)用CFURLCreateWithString
將url轉(zhuǎn)換為CFURL
對(duì)象滩褥。然后使用四個(gè)參數(shù)調(diào)用CFHTTPMessageCreateRequest
:kCFAllocatorDefault
指定默認(rèn)的系統(tǒng)內(nèi)存分配器用于創(chuàng)建消息引用病蛉,requestMethod
指定方法,例如POST方法瑰煎,myURL
指定URL铺然,例如http:// www.apple.com
和kCFHTTPVersion1_1
指定該消息的HTTP版本為1.1。
然后丢间,由CFHTTPMessageCreateRequest
返回的消息對(duì)象引用(myRequest)
隨同消息體(bodyData)
一起被發(fā)送到CFHTTPMessageSetBody
探熔。然后調(diào)用CFHTTPMessageSetHeaderFieldValue
,使用相同的消息對(duì)象引用以及頭的名稱(headerField)
和要設(shè)置的值(value)
烘挫。 header
參數(shù)是一個(gè)CFString
對(duì)象,比如Content-Length
,而value參數(shù)是一個(gè)CFString對(duì)象饮六,如1260.
最后其垄,通過(guò)調(diào)用CFHTTPMessageCopySerializedMessage
對(duì)消息進(jìn)行序列化,并且應(yīng)該通過(guò)寫入流發(fā)送給預(yù)期的接收者卤橄,在這個(gè)例子中http://www.apple.com
绿满。
注意:請(qǐng)求體通常被省略。 使用請(qǐng)求主體的主要位置在POST請(qǐng)求中以包含POST數(shù)據(jù)窟扑。 它也可以用于與HTTP擴(kuò)展相關(guān)的其他一些請(qǐng)求類型喇颁,例如
WebDAV
。 有關(guān)更多信息嚎货,請(qǐng)參閱RFC 2616橘霎。
當(dāng)消息不再需要時(shí),釋放消息對(duì)象和序列化消息殖属。 有關(guān)示例代碼姐叁,請(qǐng)參見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 - 創(chuàng)建一個(gè)CFHTTP響應(yīng)
創(chuàng)建HTTP響應(yīng)的步驟幾乎與創(chuàng)建HTTP請(qǐng)求的步驟相同洗显。 唯一的區(qū)別是不是調(diào)用CFHTTPMessageCreateRequest
外潜,而是使用相同的參數(shù)調(diào)用函數(shù)CFHTTPMessageCreateResponse
。
Deserializing an Incoming HTTP Request - 反序列化傳入的HTTP請(qǐng)求
要反序列化傳入的HTTP請(qǐng)求挠唆,請(qǐng)使用CFHTTPMessageCreateEmpty
函數(shù)創(chuàng)建一條空消息处窥,并以isRequest
參數(shù)的形式傳遞TRUE
以指定將創(chuàng)建一個(gè)空請(qǐng)求消息。 然后使用函數(shù)CFHTTPMessageAppendBytes
將傳入消息附加到空消息玄组。 CFHTTPMessageAppendBytes
將消息反序列化并移除它可能包含的任何控制信息滔驾。
繼續(xù)執(zhí)行此操作,直到函數(shù)CFHTTPMessageIsHeaderComplete
返回TRUE
巧勤。 如果您沒(méi)有檢查CFHTTPMessageIsHeaderComplete
是否返回TRUE嵌灰,則該消息可能不完整且不可靠。 在Listing 3-3
中可以看到使用這兩個(gè)函數(shù)的示例颅悉。
Listing 3-3 Deserializing a message
CFHTTPMessageRef myMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE);
if (!CFHTTPMessageAppendBytes(myMessage, &data, numBytes)) {
//Handle parsing error
}
在該示例中沽瞭,data
是要附加的數(shù)據(jù),numBytes
是data的長(zhǎng)度剩瓶。 您可能需要調(diào)用CFHTTPMessageIsHeaderComplete
來(lái)驗(yàn)證附加消息的標(biāo)題是否完整驹溃。
if (CFHTTPMessageIsHeaderComplete(myMessage)) {
// Perform processing.
}
通過(guò)反序列化消息,您現(xiàn)在可以調(diào)用以下任何函數(shù)從消息中提取信息:
-
CFHTTPMessageCopyBody
獲取消息正文的副本 -
CFHTTPMessageCopyHeaderFieldValue
獲取特定標(biāo)題字段值的副本 -
CFHTTPMessageCopyAllHeaderFields
獲得所有消息標(biāo)題字段的副本 -
CFHTTPMessageCopyRequestURL
獲取消息URL的副本 -
CFHTTPMessageCopyRequestMethod
獲取消息請(qǐng)求方法的副本
當(dāng)你不再需要信息時(shí)延曙,正確地釋放和處理它豌鹤。
Deserializing an Incoming HTTP Response - 反序列化傳入的HTTP響應(yīng)
正如創(chuàng)建HTTP請(qǐng)求與創(chuàng)建HTTP響應(yīng)非常類似,反序列化傳入的HTTP請(qǐng)求也非常類似于反序列化傳入的HTTP響應(yīng)枝缔。 唯一重要的區(qū)別是當(dāng)調(diào)用CFHTTPMessageCreateEmpty
時(shí)布疙,必須將FALSE
傳遞給isRequest
參數(shù)蚊惯,以指定要?jiǎng)?chuàng)建的消息是響應(yīng)消息。
Using a Read Stream to Serialize and Send HTTP Requests - 使用讀取流來(lái)序列化和發(fā)送HTTP請(qǐng)求
您可以使用CFReadStream
對(duì)象序列化并發(fā)送CFHTTP
請(qǐng)求灵临。 當(dāng)您使用CFReadStream
對(duì)象發(fā)送CFHTTP
請(qǐng)求時(shí)截型,打開流會(huì)導(dǎo)致消息被序列化并一步發(fā)送。 使用CFReadStream
對(duì)象發(fā)送CFHTTP請(qǐng)求可以很容易地獲得對(duì)請(qǐng)求的響應(yīng)儒溉,因?yàn)轫憫?yīng)可用作流的屬性宦焦。
1. Serializing and Sending an HTTP Request - 序列化和發(fā)送HTTP請(qǐng)求
要使用CFReadStream
對(duì)象序列化并發(fā)送HTTP請(qǐng)求,請(qǐng)首先按照Creating a CFHTTP Request中的描述創(chuàng)建CFHTTP請(qǐng)求并設(shè)置消息正文和標(biāo)頭顿涣。 然后通過(guò)調(diào)用函數(shù)CFReadStreamCreateForHTTPRequest
并傳遞剛剛創(chuàng)建的請(qǐng)求來(lái)創(chuàng)建一個(gè)CFReadStream
對(duì)象波闹。 最后,用CFReadStreamOpen
打開讀取流涛碑。
當(dāng)調(diào)用CFReadStreamCreateForHTTPRequest
時(shí)精堕,它會(huì)復(fù)制它傳遞的CFHTTP
請(qǐng)求對(duì)象。 因此锌唾,如果需要锄码,可以在調(diào)用CFReadStreamCreateForHTTPRequest之后立即釋放CFHTTP請(qǐng)求對(duì)象。
由于在創(chuàng)建CFHTTP請(qǐng)求時(shí)晌涕,讀取流將與myUrl參數(shù)指定的服務(wù)器建立套接字連接滋捶,因此在認(rèn)為該流打開之前必須允許經(jīng)過(guò)一段時(shí)間。 打開讀取流也會(huì)導(dǎo)致請(qǐng)求被序列化并發(fā)送余黎。
Listing 3-4
中可以看到如何序列化和發(fā)送HTTP請(qǐng)求的示例重窟。
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 - 檢查響應(yīng)
在運(yùn)行循環(huán)中調(diào)度請(qǐng)求之后,您最終將獲得標(biāo)題完成回調(diào)惧财。 此時(shí)巡扇,您可以調(diào)用CFReadStreamCopyProperty從讀取流獲取消息響應(yīng)。
CFHTTPMessageRef myResponse = (CFHTTPMessageRef)CFReadStreamCopyProperty(myReadStream, kCFStreamPropertyHTTPResponseHeader);
您可以通過(guò)調(diào)用函數(shù)CFHTTPMessageCopyResponseStatusLine
從響應(yīng)消息中獲取完整的狀態(tài)行:
CFStringRef myStatusLine = CFHTTPMessageCopyResponseStatusLine(myResponse);
或者通過(guò)調(diào)用函數(shù)CFHTTPMessageGetResponseStatusCode
從響應(yīng)消息中獲取狀態(tài)碼:
UInt32 myErrCode = CFHTTPMessageGetResponseStatusCode(myResponse);
注意:如果您正在同步使用此類(無(wú)需在運(yùn)行循環(huán)中調(diào)度它)垮衷,則必須在調(diào)用
CFReadStreamCopyProperty
之前至少調(diào)用一次CFReadStreamRead來(lái)讀取消息厅翔。CFReadStreamRead
調(diào)用阻塞,直到數(shù)據(jù)可用(或連接失敳笸弧)刀闷。 不要在主應(yīng)用程序線程上執(zhí)行此操作。
Handling Authentication Errors - 處理認(rèn)證錯(cuò)誤
如果函數(shù)CFHTTPMessageGetResponseStatusCode
返回的狀態(tài)代碼是401
(遠(yuǎn)程服務(wù)器需要認(rèn)證信息)或407
(代理服務(wù)器需要認(rèn)證)仰迁,則需要將認(rèn)證信息附加到請(qǐng)求并再次發(fā)送甸昏。 請(qǐng)閱讀Communicating with Authenticating HTTP Servers以獲取有關(guān)如何處理身份驗(yàn)證的信息。
Handling Redirection Errors - 處理重定向錯(cuò)誤
當(dāng)CFReadStreamCreateForHTTPRequest
創(chuàng)建讀取流時(shí)徐许,默認(rèn)情況下禁用流的自動(dòng)重定向施蜜。 如果發(fā)送請(qǐng)求的統(tǒng)一資源定位符或URL被重定向到另一個(gè)URL,則發(fā)送該請(qǐng)求將導(dǎo)致錯(cuò)誤雌隅,其狀態(tài)碼范圍為300
到307
翻默,如果收到重定向錯(cuò)誤缸沃,則需要關(guān)閉流,再次創(chuàng)建流冰蘑,為其啟用自動(dòng)重定向和泌,并打開流村缸。 參見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);
您可能希望在創(chuàng)建讀取流時(shí)啟用自動(dòng)重定向。
Canceling a Pending Request - 取消待處理的請(qǐng)求
一旦請(qǐng)求發(fā)送完畢梯皿,就無(wú)法阻止遠(yuǎn)程服務(wù)器對(duì)其執(zhí)行操作仇箱。 但是,如果您不再關(guān)心響應(yīng)數(shù)據(jù)东羹,則可以關(guān)閉流剂桥。
重要提示:當(dāng)另一個(gè)線程正在等待來(lái)自該流的內(nèi)容時(shí),不要關(guān)閉任何線程中的流属提。 如果您需要能夠終止請(qǐng)求权逗,則應(yīng)使用非阻塞I / O,如Preventing Blocking When Working with Streams中所述冤议。 確保在關(guān)閉它之前從運(yùn)行循環(huán)中移除流斟薇。
后記
本篇主要講述了與HTTP服務(wù)器通信,感興趣的給個(gè)贊或者關(guān)注~~~~