在iOS應用開發中,CFNetwork框架其實並非很是經常使用的,相對NSURLSession框架而言,這是一個相對底層的網絡工做框架。官方文檔中的下圖描述了CFNetwork在整個網絡體系中的位置:數組
CFNetwork與CoreFoundation關係密切,其實基於CoreFoundation框架的,結構以下圖所示:網絡
本篇博客中不會過多的設計CoreFoundation框架中的內容,主要總結和介紹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
在基於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);
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);
有時,客戶端在向服務端進行請求時收到狀態爲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);
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 //修改時間
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);
上面介紹的內容更多仍是關於使用CFNetWork框架進行HTTP或FTP請求的相關方法,其實CFNetWork框架中還提供了複雜的Bonjour服務功能,其與CFNetService相關,這部份內容後面有時間再進行整理總結吧。
歡迎交流 琿少 QQ 316045346