轉載自 http://blog.csdn.net/kesalin/article/details/8798039 git
http://blog.csdn.net/kesalin/article/details/8801156github
1. iOS網絡編程層次結構也分爲三層:編程
Cocoa層:NSURL,Bonjour,Game Kit,WebKit網絡
Core Foundation層:基於 C 的 CFNetwork 和 CFNetServices異步
OS層:基於 C 的 BSD socketsocket
Cocoa層是最上層的基於 Objective-C 的 API,好比 URL訪問,NSStream,Bonjour,GameKit等,這是大多數狀況下咱們經常使用的 API。Cocoa 層是基於 Core Foundation 實現的。ide
Core Foundation層:由於直接使用 socket 須要更多的編程工做,因此蘋果對 OS 層的 socket 進行簡單的封裝以簡化編程任務。該層提供了 CFNetwork 和 CFNetServices,其中 CFNetwork 又是基於 CFStream 和 CFSocket。函數
OS層:最底層的 BSD socket 提供了對網絡編程最大程度的控制,可是編程工做也是最多的。所以,蘋果建議咱們使用 Core Foundation 及以上層的 API 進行編程。oop
實際項目中,常使用成熟的第三方網絡庫:spa
(1)ASIHTTPRequest,中止更新,應該放棄。
(2)AFNetworking,輕量級。項目源碼: https://github.com/AFNetworking/AFNetworking
2. CFNetwork API 簡介
CFNetwork 接口是基於 C 的,下面的接口用於建立一對 socket stream,一個用於讀取,一個用於寫入:
void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream);
該函數使用 host 以及 port,CFNetwork 會將該 host 轉換爲 IP 地址,並轉換爲網絡字節順序。若是咱們只須要一個 socket stream,咱們能夠將另一個設置爲 NULL。還有另外兩個「重載」的建立 socket sream的接口:CFStreamCreatePairWithSocket 和 CFStreamCreatePairWithPeerSocketSignature,在這裏就不一一介紹了。
注意:在使用這些 socket stream 以前,必須顯式地調用其 open 函數:
Boolean CFReadStreamOpen(CFReadStreamRef stream); Boolean CFWriteStreamOpen(CFWriteStreamRef stream);
但與 socket 不一樣的是,這兩個接口是異步的,當成功 open 以後,若是調用方設置了獲取 kCFStreamEventOpenCompleted 事件的標誌的話就會其調用回調函數。
而該回調函數及其參數設置是經過以下接口進行的:
Boolean CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext); Boolean CFWriteStreamSetClient(CFWriteStreamRef stream, CFOptionFlags streamEvents, CFWriteStreamClientCallBack clientCB, CFStreamClientContext *clientContext);
該函數用於設置回調函數及相關參數。經過 streamEvents 標誌來設置咱們對哪些事件感興趣;clientCB 是一個回調函數,當事件標誌對應的事件發生時,該回調函數就會被調用;clientContext 是用於傳遞參數到回調函數中去。
當設置好回調函數以後,咱們能夠將 socket stream 當作事件源調度到 run-loop 中去,這樣 run-loop 就能分發該 socket stream 的網絡事件了。
void CFReadStreamScheduleWithRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); void CFWriteStreamScheduleWithRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode);
注意,在咱們再也不關心該 socket stream 的網絡事件時,記得要調用以下接口將 socket stream 從 run-loop 的事件源中移除。
void CFReadStreamUnscheduleFromRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); void CFWriteStreamUnscheduleFromRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode);
當咱們將 socket stream 的網絡事件調度到 run-loop 以後,咱們就能在回調函數中相應各類事件,好比 kCFStreamEventHasBytesAvailable 讀取數據:
Boolean CFReadStreamHasBytesAvailable(CFReadStreamRef stream); CFIndex CFReadStreamRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength);
或 kCFStreamEventCanAcceptBytes 寫入數據:
Boolean CFWriteStreamCanAcceptBytes(CFWriteStreamRef stream); CFIndex CFWriteStreamWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength);
最後,咱們調用 close 方法關閉 socket stream:
void CFReadStreamClose(CFReadStreamRef stream); void CFWriteStreamClose(CFWriteStreamRef stream);