iOS開發之CFNetwork框架使用

iOS開發之CFNetwork框架使用

1、引言

    在iOS應用開發中,CFNetwork框架其實並非很是經常使用的,相對NSURLSession框架而言,這是一個相對底層的網絡工做框架。官方文檔中的下圖描述了CFNetwork在整個網絡體系中的位置:數組

CFNetwork與CoreFoundation關係密切,其實基於CoreFoundation框架的,結構以下圖所示:網絡

本篇博客中不會過多的設計CoreFoundation框架中的內容,主要總結和介紹CFNetwork的相關內容與簡單應用。框架

2、使用CFNetwork進行簡單的網絡請求

      CFNetwork是使用C語言實現的一套網絡訪問框架,進行一個簡單的網絡請求示例代碼以下:函數

//建立請求方法字符串    
CFStringRef method = CFSTR("GET");
//建立請求URL字符串
CFStringRef urlStr = CFSTR("http://www.baidu.com");
//建立請求URL對象
CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, urlStr, NULL);
//建立HTTP消息對象
CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, method, url, kCFHTTPVersion1_1);
//進行請求頭的設置
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("key"), CFSTR("Value"));
//建立讀取流對象
CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
//定義讀取流上下文
CFStreamClientContext ctxt = {0, (__bridge void *)(self), NULL, NULL, NULL};
//設置讀取的客服端 即回調相關
CFReadStreamSetClient(readStream, kCFStreamEventHasBytesAvailable|kCFStreamEventEndEncountered|kCFStreamEventOpenCompleted|kCFStreamEventCanAcceptBytes|kCFStreamEventErrorOccurred, myCallBack, &ctxt);
//將讀取流加入runloop中
CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
//開啓流
printf("%d",CFReadStreamOpen(readStream));

實現myCallBack回調函數以下:oop

void myCallBack (CFReadStreamRef stream,CFStreamEventType type,void *clientCallBackInfo){
    //流中有可讀數據的回調  
    if (type == kCFStreamEventHasBytesAvailable) {      
        //將流中的數據存入到數組中
        UInt8 buff [1024];
        CFReadStreamRead(stream, buff, 1024);
        printf("%s",buff);
    //流打開完成的回調
    }else if(type==kCFStreamEventOpenCompleted){
        NSLog(@"open");
    //流異常的回調
    }else if (type==kCFStreamEventErrorOccurred){
        NSLog(@"error:%@",CFErrorCopyDescription( CFReadStreamCopyError(stream)));
    //能夠接收寫數據時調用
    }else if (type==kCFStreamEventCanAcceptBytes){
        NSLog(@"kCFStreamEventCanAcceptBytes");
    //讀取結束回調
    }else if(type==kCFStreamEventEndEncountered){
        NSLog(@"end");
        //關閉流
        CFReadStreamClose(stream);
        //將流從runloop中移除
        CFReadStreamUnscheduleFromRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
    }
}

上面演示了簡單的GET請求,若是使用的請求方法爲POST,則能夠進行請求體的設置,上面示例代碼中,CFStringRef、CFURLRef、CFReadStreamRef等相關的類爲CoreFoundation框架中的,這裏暫不深究,簡單使用便可。後面咱們將詳細的探討CFNetwork中相關類的使用。ui

3、CFHTTPMessageRef詳解

    在基於C的框架中,類對象都是使用結構體指針描述的,CFHTTPMessageRef是HTTP消息的封裝,其能夠是一個HTTP請求,也能夠是一個HTTP回執。與其相關的方法解析以下:url

//返回CGHTTPMessageRef的類型ID
CFTypeID CFHTTPMessageGetTypeID(void);
//建立一個HTTP請求消息
/*
alloc爲內存管理器 通常使用默認的kCFAllocatorDefault
requestMethod爲請求方法
url爲請求的路徑
httpVersion爲請求的HTTP版本
HTTP版本定義以下:
kCFHTTPVersion1_0
kCFHTTPVersion1_1
kCFHTTPVersion2_0
*/
CFHTTPMessageRef CFHTTPMessageCreateRequest(CFAllocatorRef __nullable alloc, CFStringRef requestMethod, CFURLRef url, CFStringRef httpVersion);
//建立一個HTTP回執消息
/*
alloc內存管理器
statusCode 請求回執狀態碼
statusDescription 請求回執狀態描述
httpVersion HTTP版本號
*/
CFHTTPMessageRef CFHTTPMessageCreateResponse(CFAllocatorRef  __nullable alloc,CFIndex         statusCode,CFStringRef     __nullable statusDescription,CFStringRef     httpVersion);
//建立一個空的HTTP消息 
/*
isRequest 若是傳入kCFBooleanTrue 則爲請求類型 不然爲回執類型
*/
CFHTTPMessageRef CFHTTPMessageCreateEmpty(CFAllocatorRef __nullable alloc, Boolean isRequest);
//複製一個HTTP消息
CFHTTPMessageRef CFHTTPMessageCreateCopy(CFAllocatorRef __nullable alloc, CFHTTPMessageRef message);
//判斷一個HTTP消息是請求 仍是 回執
Boolean CFHTTPMessageIsRequest(CFHTTPMessageRef message);
//獲取HTTP版本
CFStringRef CFHTTPMessageCopyVersion(CFHTTPMessageRef message);
//獲取消息體內容 請求體或者回執體
CFDataRef CFHTTPMessageCopyBody(CFHTTPMessageRef message);
//設置消息體內容
void CFHTTPMessageSetBody(CFHTTPMessageRef message, CFDataRef bodyData);
//獲取某個消息頭內容
CFStringRef CFHTTPMessageCopyHeaderFieldValue(CFHTTPMessageRef message, CFStringRef headerField);
//獲取全部消息頭字段
CFDictionaryRef CFHTTPMessageCopyAllHeaderFields(CFHTTPMessageRef message);
//設置消息頭
void CFHTTPMessageSetHeaderFieldValue(CFHTTPMessageRef message, CFStringRef headerField, CFStringRef __nullable value);
//向空消息中追加序列化的數據
Boolean CFHTTPMessageAppendBytes(CFHTTPMessageRef message, const UInt8 *newBytes, CFIndex numBytes);
//返回消息頭數據是否準備完成
Boolean CFHTTPMessageIsHeaderComplete(CFHTTPMessageRef message);
//將一個消息對象序列化成數據
CFDataRef CFHTTPMessageCopySerializedMessage(CFHTTPMessageRef message);
/*=================下面這些方法針對於請求類型的消息=====================*/
//獲取消息中的url
CFURLRef CFHTTPMessageCopyRequestURL(CFHTTPMessageRef request);
//獲取消息的請求方法
CFStringRef CFHTTPMessageCopyRequestMethod(CFHTTPMessageRef request);
//添加認證信息
Boolean CFHTTPMessageAddAuthentication(CFHTTPMessageRef   request,CFHTTPMessageRef   __nullable authenticationFailureResponse,CFStringRef        username,CFStringRef        password,CFStringRef        __nullable authenticationScheme,Boolean            forProxy);
/*=================下面這些方法針對於繪製類型的消息=====================*/
//獲取回執狀態碼
CFIndex CFHTTPMessageGetResponseStatusCode(CFHTTPMessageRef response);
//獲取回執狀態行信息
CFStringRef CFHTTPMessageCopyResponseStatusLine(CFHTTPMessageRef response);

4、進行請求與回調處理

    CFHTTPMessageRef的主要用途是構建出HTTP的請求或回執對象,請求的相關發起與回調方法都封裝在CFHTTPStream.h這個頭文件中,解析以下:spa

//經過一個HTTP請求建立一個讀取流對象
CFReadStreamRef CFReadStreamCreateForHTTPRequest(CFAllocatorRef __nullable alloc, CFHTTPMessageRef request);
//經過一個HTTP請求建立讀取流對象 可是請求的body會被忽略 取requestBody做爲請求體
CFReadStreamRef CFReadStreamCreateForStreamedHTTPRequest(CFAllocatorRef __nullable alloc, CFHTTPMessageRef requestHeaders, CFReadStreamRef requestBody);
//設置讀取流是否自動重定向
void CFHTTPReadStreamSetRedirectsAutomatically(CFReadStreamRef httpStream, Boolean shouldAutoRedirect);

5、關於請求的證書驗證

    有時,客戶端在向服務端進行請求時收到狀態爲401的回執,這時每每代表須要客戶端提供用戶憑證,在CFNetWork框架中,用戶憑證與證書驗證相關方法封裝在CFHTTPAuthentication.h頭文件中。解析以下:設計

//獲取CFHTTPAuthentication類ID
CFTypeID CFHTTPAuthenticationGetTypeID(void);
/*
經過一個401或者407的請求回執建立一個 用戶認證對象
*/
CFHTTPAuthenticationRef CFHTTPAuthenticationCreateFromResponse(CFAllocatorRef __nullable alloc, CFHTTPMessageRef response);
//獲取一個用戶認證對象是否有效
Boolean CFHTTPAuthenticationIsValid(CFHTTPAuthenticationRef auth, CFStreamError * __nullable error);
//獲取某個用戶認證對象是不是某個請求的
Boolean CFHTTPAuthenticationAppliesToRequest(CFHTTPAuthenticationRef auth, CFHTTPMessageRef request);
//獲取某個用戶認證是否必須有序進行
Boolean CFHTTPAuthenticationRequiresOrderedRequests(CFHTTPAuthenticationRef auth);
//使用給定的用戶名和密碼執行證書驗證方法
Boolean CFHTTPMessageApplyCredentials(CFHTTPMessageRef request,CFHTTPAuthenticationRef auth,CFStringRef  __nullable username,CFStringRef  __nullable	password,CFStreamError *   __nullable	error);
/*
此方法和上面方法做用一致 不一樣的是使用字典來進行用戶名和密碼的設置 字段的鍵以下:
kCFHTTPAuthenticationUsername 用戶名鍵
kCFHTTPAuthenticationPassword 密碼鍵
kCFHTTPAuthenticationAccountDomain 帳戶域
*/
Boolean CFHTTPMessageApplyCredentialDictionary(CFHTTPMessageRef request,CFHTTPAuthenticationRef   auth,CFDictionaryRef dict,CFStreamError * __nullable  error);
//返回用戶憑證的帳戶域
CFStringRef CFHTTPAuthenticationCopyRealm(CFHTTPAuthenticationRef auth);
//返回一組帳戶域
CFArrayRef CFHTTPAuthenticationCopyDomains(CFHTTPAuthenticationRef auth);
//返回用戶憑證的驗證方法
CFStringRef CFHTTPAuthenticationCopyMethod(CFHTTPAuthenticationRef auth);
//獲取用戶憑證驗證是否須要用戶名和密碼
Boolean CFHTTPAuthenticationRequiresUserNameAndPassword(CFHTTPAuthenticationRef auth);
//獲取是否須要帳戶域
Boolean CFHTTPAuthenticationRequiresAccountDomain(CFHTTPAuthenticationRef auth);

6、進行FTP協議的數據交換

    CFNetWork框架也支持與FTP協議的服務端進行數據交互,方法解析以下:代理

//根據URL建立FTP讀取流對象 用來進行文件下載
CFReadStreamRef CFReadStreamCreateWithFTPURL(CFAllocatorRef __nullable alloc, CFURLRef ftpURL);
//解析文件或目錄的格式化數據
CFIndex CFFTPCreateParsedResourceListing(CFAllocatorRef __nullable alloc, const UInt8 *buffer, CFIndex bufferLength, CFDictionaryRef __nullable *  __nullable parsed);
//根據URL建立一個FTP寫入流對象 用來進行文件上傳
CFWriteStreamRef CFWriteStreamCreateWithFTPURL(CFAllocatorRef __nullable alloc, CFURLRef ftpURL);

對於FTP寫入和讀取流來講,可使用CFReadStreamSetProperty()函數或者CFWriteStreamSetProperty()函數來進行屬性的設置,可設置的屬性列舉以下:

kCFStreamPropertyFTPUserName //設置用戶名
kCFStreamPropertyFTPPassword //設置密碼
kCFStreamPropertyFTPUsePassiveMode //布爾值  設置是否被動模式
kCFStreamPropertyFTPResourceSize  //資源大小
kCFStreamPropertyFTPFileTransferOffset //記錄文件位置 用來斷點續傳
kCFStreamPropertyFTPAttemptPersistentConnection //是否重用鏈接
kCFStreamPropertyFTPProxy  //設置代理字典
kCFStreamPropertyFTPFetchResourceInfo //資源詳情字典
//下面爲代理字典中能夠定義的鍵
kCFStreamPropertyFTPProxyHost  //代理主機
kCFStreamPropertyFTPProxyPort  //代理端口
kCFStreamPropertyFTPProxyUser  //代理用戶名
kCFStreamPropertyFTPProxyPassword //代理密碼
//下面是資源詳情字典中能夠定義的鍵
kCFFTPResourceMode  //資源模式
kCFFTPResourceName //資源名
kCFFTPResourceOwne //資源全部者
kCFFTPResourceGroup //資源組
kCFFTPResourceLink  //資源連接
kCFFTPResourceSize  //資源尺寸
kCFFTPResourceType  //資源類型
kCFFTPResourceModDate //修改時間

7、主機地址相關操做

    CFNetWork中也封裝了與主機地址域名相關的操做方法,例如,咱們能夠經過域名進行DNS解析出IP地址,示例代碼以下:

#import <netinet/in.h>
#import <arpa/inet.h>
CFStringRef hostString = CFSTR("www.baidu.com");
CFHostRef host = CFHostCreateWithName(CFAllocatorGetDefault(), hostString);
CFHostStartInfoResolution(host, kCFHostAddresses, NULL);
CFArrayRef addresses = CFHostGetAddressing(host, NULL);
for (int i = 0; i<CFArrayGetCount(addresses); i++) {
    struct sockaddr_in * ip;
    ip = (struct sockaddr_in *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
    printf("%s\n",inet_ntoa(ip->sin_addr));
}

CFHostRef對象操做相關方法解析以下:

//獲取類型ID
CFTypeID CFHostGetTypeID(void);
//根據域名建立CFHostRef對象
CFHostRef CFHostCreateWithName(CFAllocatorRef __nullable allocator, CFStringRef hostname);
/*
根據地址建立CFHostRef對象
addr參數爲sockaddr結構體數據
*/
CFHostRef CFHostCreateWithAddress(CFAllocatorRef __nullable allocator, CFDataRef addr);
//CFHostRef對象的複製
CFHostRef CFHostCreateCopy(CFAllocatorRef __nullable alloc, CFHostRef host);
//對指定主機進行信息預查找 返回值標明是否查找成功
Boolean CFHostStartInfoResolution(CFHostRef theHost, CFHostInfoType info, CFStreamError * __nullable error);
//獲取主機的地址列表 數組中爲sockaddr結構體數據
CFArrayRef CFHostGetAddressing(CFHostRef theHost, Boolean * __nullable hasBeenResolved);
//獲取主機名列表
CFArrayRef CFHostGetNames(CFHostRef theHost, Boolean * __nullable hasBeenResolved);
//獲取主機可達性信息
CFDataRef CFHostGetReachability(CFHostRef theHost, Boolean * __nullable hasBeenResolved);
//取消未完成的解析
/*
解析類型枚舉
typedef CF_ENUM(int, CFHostInfoType) {
  //地址
  kCFHostAddresses			  = 0,
  //主機名
  kCFHostNames				  = 1,
  //可達性信息
  kCFHostReachability		   = 2
};
*/
void CFHostCancelInfoResolution(CFHostRef theHost, CFHostInfoType info);
//設置客戶端回調
Boolean CFHostSetClient(CFHostRef theHost, CFHostClientCallBack __nullable clientCB, CFHostClientContext * __nullable clientContext);
//註冊進Runloop
void CFHostScheduleWithRunLoop(CFHostRef theHost, CFRunLoopRef runLoop, CFStringRef runLoopMode);
//從Runloop中註銷
void CFHostUnscheduleFromRunLoop(CFHostRef theHost, CFRunLoopRef runLoop, CFStringRef runLoopMode);

8、後續

    上面介紹的內容更多仍是關於使用CFNetWork框架進行HTTP或FTP請求的相關方法,其實CFNetWork框架中還提供了複雜的Bonjour服務功能,其與CFNetService相關,這部份內容後面有時間再進行整理總結吧。

歡迎交流  琿少 QQ 316045346

相關文章
相關標籤/搜索