CFNetwork框架詳細(xì)解析(五) —— CFNetwork編程指導(dǎo)之與HTTP服務(wù)器通信(四)

版本記錄

版本號(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)用CFHTTPMessageCreateRequestkCFAllocatorDefault指定默認(rèn)的系統(tǒng)內(nèi)存分配器用于創(chuàng)建消息引用病蛉,requestMethod指定方法,例如POST方法瑰煎,myURL指定URL铺然,例如http:// www.apple.comkCFHTTPVersion1_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)碼范圍為300307翻默,如果收到重定向錯(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)注~~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末恕酸,一起剝皮案震驚了整個(gè)濱河市堪滨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蕊温,老刑警劉巖袱箱,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異义矛,居然都是意外死亡发笔,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門凉翻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)了讨,“玉大人,你說(shuō)我怎么就攤上這事噪矛×咳铮” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵艇挨,是天一觀的道長(zhǎng)残炮。 經(jīng)常有香客問(wèn)我,道長(zhǎng)缩滨,這世上最難降的妖魔是什么势就? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任泉瞻,我火速辦了婚禮,結(jié)果婚禮上苞冯,老公的妹妹穿的比我還像新娘袖牙。我一直安慰自己,他們只是感情好舅锄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布鞭达。 她就那樣靜靜地躺著,像睡著了一般皇忿。 火紅的嫁衣襯著肌膚如雪畴蹭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天鳍烁,我揣著相機(jī)與錄音叨襟,去河邊找鬼。 笑死幔荒,一個(gè)胖子當(dāng)著我的面吹牛糊闽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爹梁,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼右犹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了卫键?” 一聲冷哼從身側(cè)響起傀履,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎莉炉,沒(méi)想到半個(gè)月后钓账,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡絮宁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年梆暮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绍昂。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡啦粹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窘游,到底是詐尸還是另有隱情唠椭,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布忍饰,位于F島的核電站贪嫂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏艾蓝。R本人自食惡果不足惜力崇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一斗塘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧亮靴,春花似錦馍盟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春揪罕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背垂寥。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工主届, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人尿孔。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓俊柔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親活合。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雏婶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容