iOS:網絡編程模式

 IOS 可以採用三類api 接口進行網絡編程,根據抽象層次從低到高分別爲socket方式、stream方式、url 方式。

一 、socket 方式

       IOS 提供的socket 方式的網絡編程接口爲CFSocket。CFSocket是BSD sockets的抽象和封裝,CFSocket提供BSD sockets幾乎所有的功能,並與run loop集成,用來實現多線程網絡編程和網絡事件監聽。基於 CFSocket可以實現各種類型的 socket編程,包括stream-based 的sockets(如tcp)和packet-based 的sockets(如udp)。需要注意的是在iOS中CFSocket接口在需要時不自動激活設備的 cellular modem或on-demand VPN。

      CFSocket包括以下編程接口,包括Socket的 創建、配置,以及根據創建和配置好的Socket 進行 遠程通訊等接口。

1  Socket的 創建

1 .1、CFSocketCreate

創建一個特定協議和類型的 CFSocket對象

1.2、CFSocketCreateWithSocketSignature

該接口根據一個包含通訊協議和地址的CFSocketSignature結構來創建一個CFSocket對象


1.3、 CFSocketCreateConnectedToSocketSignature

該接口在創建一個CFSocket對象的同時還與一個遠端主機進行連接。


1.4、CFSocketCreateWithNative

該接口通過封裝一個存在的 BSD socket來創建一個CFSocket對象。


2   Socket的配置

2.1   CFSocketCopyAddress

功能: 返回一個 CFSocket對象的本地地址。

語法:

SWIFT

func CFSocketCopyAddress(_ s: CFSocket!) -> CFData!

2.2、CFSocketCopyPeerAddress

功能:返回與一個 CFSocket對象連接的遠端地址。

語法:

SWIFT

func CFSocketCopyPeerAddress(_ s: CFSocket!) -> CFData!

2.3  CFSocketDisableCallBacks

功能:臨時取消一個CFSocket對象創建時指定的某種類型的事件回調。

語法:

SWIFT

func CFSocketDisableCallBacks(_ s: CFSocket!,

                            _ callBackTypes: CFOptionFlags)

2.4   CFSocketEnableCallBacks

功能:重新允許先前CFSocketDisableCallBacks函數取消的某種類型的事件回調。

語法:

SWIFT

func CFSocketEnableCallBacks(_ s: CFSocket!,

                           _ callBackTypes: CFOptionFlags)

2.5 CFSocketGetContext

功能:返回一個CFSocket對象的上下文信息。

語法:

SWIFT

func CFSocketGetContext(_ s: CFSocket!,

                      _ context: UnsafeMutablePointer<CFSocketContext>)

2.6 CFSocketGetNative

返回與一個CFSocket對象相關的本地 BSD socket。

語法:

SWIFT

func CFSocketGetNative(_ s: CFSocket!) -> CFSocketNativeHandle

2.7   CFSocketGetSocketFlags

功能:返回控制一個CFSocket對象的確定行爲的 標誌。

語法:

SWIFT

func CFSocketGetSocketFlags(_ s: CFSocket!) -> CFOptionFlags


2.8 CFSocketSetSocketFlags

功能:設置控制一個CFSocket對象的確定行爲的 標誌。

語法:

SWIFT

func CFSocketSetSocketFlags(_ s: CFSocket!,

                          _ flags: CFOptionFlags)

2.9 CFSocketSetAddress

語法:

SWIFT

func CFSocketSetAddress(_ s: CFSocket!,

                      _ address: CFData!) -> CFSocketError

功能:爲一個CFSocket對象綁定一個本地地址並在本地socket支持的情況下對socket進行配置使其處於監聽狀態。該函數對應本地socket的 bind以及listen功能。一旦CFSocket對象綁定地址,依賴於socket的協議,其它進程和主機能連接到該CFSocket對象。


3、Sockets的使用

3.1 CFSocketConnectToAddress

功能:打開與一個遠程socket的一個連接。

語法:

SWIFT

func CFSocketConnectToAddress(_ s: CFSocket!,

                            _ address: CFData!,

                            _ timeout: CFTimeInterval) -> CFSocketError

3.2 CFSocketCreateRunLoopSource

語法:

SWIFT

func CFSocketCreateRunLoopSource(_ allocator: CFAllocator!,

                               _ s: CFSocket!,

                               _ order: CFIndex) -> CFRunLoopSource!

功能:爲一個CFSocket對象創建一個CFRunLoopSource對象。該創建的 CFRunLoopSource對象不自動添加到一個run loop。爲了增加該run loop source到某個run loop,需要調用CFRunLoop對象 的CFRunLoopAddSource函數來爲該CFRunLoop對象添加run loop source。

3.3 CFSocketGetTypeID

功能:返回CFSocket對象的 opaque類型對應的類型標示符。

語法:

SWIFT

func CFSocketGetTypeID() -> CFTypeID 

3.4  CFSocketInvalidate

功能:使一個CFSocket對象無效,使其停止接收和發送任何消息。

語法:

SWIFT

func CFSocketInvalidate(_ s: CFSocket!)

3.5 CFSocketIsValid

功能:返回一個指示一個CFSocket對象是否有效及是否能夠發送和接收消息的布爾值。

語法:

SWIFT

func CFSocketIsValid(_ s: CFSocket!) -> Boolean

3.6 CFSocketSendData

功能:該函數用來通過一個CFSocket對象發送數據。

語法:

SWIFT

func CFSocketSendData(_ s: CFSocket!,

                    _ address: CFData!,

                    _ data: CFData!,

                    _ timeout: CFTimeInterval) -> CFSocketError


二、stream編程模式

       stream編程模式提供了與 unix 的文件操作類似的模式。首先創建和設置流,接着打開流,然後讀寫流,在流存在時還可以通過查詢流的相關屬性來讀取流的相關信息,在流使用完畢後關閉流。

       iOS 爲stream編程模式提供的api編程接口包括兩大類,一類是Core Foundation框架層用C語言實現的CFStream  API(包括CFStream、 CFReadStream 、CFWriteStream等),一類是基於其上的在Foundation框架層用Objective-C語言實現的NSStream API(包括NSStream、NSInputStream NSOutputStream等),兩者提供相似的接口和行爲,其中某些對象是toll-free bridged類型的,如CFStream 與NSStream,CFReadStream與NSInputStream,CFWriteStream與NSOutputStream之間,因此可以混合使用。

       開發人員可以根據自己的語言偏好選擇使用。

       CFStream API的主要接口:

1、CFStream 創建接口

1.1  CFStreamCreatePairWithPeerSocketSignature

          功能:創建一對到一個socket的可讀和可寫流。

1.2 CFStreamCreatePairWithSocketToHost

功能:創建連接到一個特定主機的特定端口的一對可讀寫流。

1.3 CFStreamCreatePairWithSocket

功能:創建一對連接到一個socket的可讀寫流

1.4 CFStreamCreateBoundPair

功能:創建一對讀寫流。

其它可讀寫流創建接口:

 1.5 CFReadStreamCreateForHTTPRequest

   功能:爲一個CFHTTP請求創建一個可讀流。

1.6 CFReadStreamCreateForStreamedHTTPRequest

功能:爲一個HTTP請求的body保持在內存的CFHTTP請求創建一個可讀流。

1.7  CFReadStreamCreateWithFTPURL

功能:創建一個FTP可讀流

1.8  CFWriteStreamCreateWithFTPURL

功能:創建一個FTP可讀流

2. CFReadStream接口

2.1 流的打開和關閉

      CFReadStreamOpen

       CFReadStreamClose

2.2  讀取數據

      CFReadStreamRead

2.3.  調度一個可讀流

  CFReadStreamScheduleWithRunLoop(_:_:_:) 

           CFReadStreamUnscheduleFromRunLoop(_:_:_:) 

2.4 檢查可讀流的屬性  

CFReadStreamCopyProperty(_:_:) 

CFReadStreamGetBuffer(_:_:_:) 

CFReadStreamCopyError(_:) 

CFReadStreamGetError(_:) 

CFReadStreamGetStatus(_:) 

CFReadStreamHasBytesAvailable(_:) 

2.5 設置可讀流的屬性

CFReadStreamSetClient(_:_:_:_:)

CFReadStreamSetProperty(_:_:_:) 

 2.6 得到 CFReadStream的 Type ID

                    CFReadStreamGetTypeID()

3.CFWriteStream 相關接口

     3.1 CFWriteStreamClose(_:)

     3.2 CFWriteStreamOpen(_:) 

     3.3 CFWriteStreamWrite(_:_:_:)

        3.4 CFWriteStreamScheduleWithRunLoop(_:_:_:)

  3.5 CFWriteStreamUnscheduleFromRunLoop(_:_:_:)

        3.6 CFWriteStreamCanAcceptBytes(_:)

      3.7 CFWriteStreamCopyProperty(_:_:)

      3.8 CFWriteStreamCopyError(_:)

      3.9 CFWriteStreamGetError(_:)

      3.10 CFWriteStreamGetStatus(_:) 

         3.11 CFWriteStreamSetClient(_:_:_:_:)

  3.12 CFWriteStreamSetProperty(_:_:_:) 

      3.13 CFWriteStreamGetTypeID()

CFStream API的使用步驟:

1) 利用流創建接口創建相關流;

2)、調用CFReadStreamSetClient (可讀流)或CFWriteStreamSetClient (可寫流)來登記要接收的流相關的事件;

3)、調用CFReadStreamScheduleWithRunLoop(可讀流)或CFWriteStreamScheduleWithRunLoop(可寫流)來使在流在一個run loop上進行調度以便接收相關事件;

4)、調用CFReadStreamOpen 或CFWriteStreamOpen 來打開已創建的流;

5)、在讀取流的創建時登記的回調中,在接收到kCFStreamEventHasBytesAvailable事件時來讀取數據, 在可寫流已登記的回調中,在接收到kCFStreamEventCanAcceptBytes 事件時開始發送數據或請求;

6) 數據傳輸完成,關閉和釋放打開和創建的相關流;

2、NSStream   API的使用

 在ios 中由於NSStream類不支持 與一個遠程主機連接,而CFStream支持,因此爲了使用 NSStream,你需要使用流創建函數CFStreamCreatePairWithSocketToHost或CFStreamCreatePairWithSocketToCFHost來打開一個與遠程主機連接的socket並分配一對CFStream 對象(CFReadStream和CFWriteStream),並cast這些對象到NSStream 對象(對應NSInputStream 和 NSOutputStream)。從而可以使用NSStream類的相關接口進行相關網絡編程。如設置接收網絡事件的代理對象,調度到當前的run loop,然後打開它們進行相應處理。

代碼片段如下:

  1. {  
  2.   
  3.         NSURL *website = [NSURL URLWithString:urlStr];  
  4.   
  5.         if (!website) {  
  6.   
  7.             NSLog(@"%@ is not a valid URL");  
  8.   
  9.             return;  
  10.   
  11.         }  
  12.   
  13.         CFReadStreamRef readStream;  
  14.   
  15.         CFWriteStreamRef writeStream;  
  16.   
  17.         CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 80, &readStream, &writeStream);  
  18.   
  19.         NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;  
  20.   
  21.         NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream;  
  22.   
  23.         [inputStream setDelegate:self];  
  24.   
  25.         [outputStream setDelegate:self];  
  26.   
  27.         [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];  
  28.   
  29.         [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];  
  30.   
  31.         [inputStream open];  
  32.   
  33.         [outputStream open];  
  34.   
  35.   
  36.         /* Store a reference to the input and output streams so that 
  37.  
  38.            they don't go away.... */  
  39.   
  40.         ...  
  41.   
  42. }  

       在NSStream對象打開後,當接收到相關的stream-event網絡消息,其代理對象中的handleEvent: 函數被調用,從而進行流相關的網絡消息處理,  如發送相關協議的請求或接收應答等。以下爲handleEvent: 函數進行事件處理的代碼片段:

   

  1. - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {  
  2.   
  3.     NSLog(@"stream:handleEvent: is invoked...");  
  4.   
  5.    
  6.   
  7.     switch(eventCode) {  
  8.   
  9.         case NSStreamEventHasSpaceAvailable:  
  10.   
  11.         {  
  12.   
  13.             if (stream == oStream) {  
  14.   
  15.                 NSString * str = [NSString stringWithFormat:  
  16.   
  17.                     @"GET / HTTP/1.0\r\n\r\n"];  
  18.   
  19.                 const uint8_t * rawstring =  
  20.   
  21.                     (const uint8_t *)[str UTF8String];  
  22.   
  23.                 [oStream write:rawstring maxLength:strlen(rawstring)];  
  24.   
  25.                 [oStream close];  
  26.   
  27.             }  
  28.   
  29.             break;  
  30.   
  31.         }  
  32.   
  33.         // continued ...  
  34.   
  35.     }  
  36.   
  37. }  

三、url 編程模式

         url 編程模式通過URL 的方式來實現網絡編程,任何要存取的網絡資源(包括局域網和廣域網)都可以用一個URL來表示和存取,並支持設備間的資源共享。url 編程模式系統提供http, https, file, ftp, data等五種協議支持,並允許用戶自己開發和登記相關類來支持另外的應用層網絡協議,進行協議的擴展。

        url 編程模式在IOS系統可以使用兩種編程接口:NSURLSession 和NSURLConnection。

        對於iOS 7 以後的最新系統推薦使用NSURLSession API,對於老版本由於不支持NSURLSession,因此必須使用NSURLConnection API。

NSURLSession編程模式是對相關的連接請求通過一個會話來完成,應用通過創建一系列sessions來實現網絡通訊,每一個session協調一組相關數據的傳輸任務。在每一個session內,應用添加一系列任務,每一個任務表現一個特定URL 請求。

        NSURLSession相比NSURLConnection的優點是支持在應用掛起、停止或crashed時能夠在後臺繼續下載數據,即支持任務的取消、重啓(恢復)、掛起,以及支持從已掛起、取消或失敗的下載中重新恢復下載的能力。

         對於簡單的請求,還可以直接通過一個簡單的NSURL對象來發出請求,並使用一個NSData內存對象或者一個文件的方式來引出NSURL指向的內容。而NSURLConnection API只能通過構造一個NSURLRequest對象或其子類來發出URL請求來請求下載或上傳URL數據。 使用一個NSURLRequest請求對象封裝一個URL請求,例如HTTP協議方法,除了可以封裝一些協議特定的屬性外,還可以規定任意本地cached數據的使用策略。

         對於NSURLRequest請求對象的應答包括兩部分:描述內容的元數據metadata及內容數據本身。兩種API對於使用NSURLRequest請求接收的元數據metadata都由NSURLResponse類來封裝,其中包含MIME類型、內容長度、編碼及提供應答的URL等內容。NSURLResponse協議特定的子類還能提供額外的元數據,如NSHTTPURLResponse提供協議頭和WEB服務器返回的狀態碼 等信息。

         NSURLSession API的使用:

         NSURLSession類支持三種會話類型(默認會話類型、臨時會話、後臺會話)以及三種類型的任務(數據任務、下載任務、上傳任務)

         數據任務使用NSData 對象來發送和接收內存數據,不存儲數據到一個文件,因此不支持後臺會話。

         下載任務以一個文件的形式引出數據,並支持在應用沒有運行時的後臺下載。

         上傳任務用來上傳數據(文件),也能夠支持應用沒有運行時的後臺上傳。

          默認會話和後臺會話的區別是後臺會話使用一個分離的進程處理所有的數據傳輸任務,並帶有一些限制:後臺會話必須使用特定應用代理來提供事件提交,並僅支持HTTP和HTTPS 協議,不支持其它定製協議,並僅支持上傳和下載任務,不支持數據任務。

         臨時會話不存儲任何數據到磁盤,所有接收的內容都保存到與會話關聯的RAM中,當會話無效時,RAM中接收的內容自動被清除。

   NSURLSession API的使用步驟:

   1 、創建一個NSURLSessionConfiguration配置對象

NSURLSessionConfiguration配置對象提供廣泛的配置選項,包括:

1)、特定於單個會話的私有數據存儲,包括caches, cookies, credentials, 和protocols;

2)、與一個特定請求或一個會話關聯的Authentication;

3)、與一個主機的最大連接數;

4)、與一個資源關聯的超時;

5)、最小和最大TLS版本支持;

6)、定製的代理詞典;

7)、cookie策略的控制;

8)、HTTP pipelining行爲的控制

2、根據配置創建相應的NSURLSession;

       如下代碼片段展示了根據不同的配置對象創建不同類型的NSURLSession會話對象。

         

  1. /* Create a session for each configurations. */  
  2.     self.defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegateself delegateQueue: [NSOperationQueue mainQueue]];  
  3.     self.backgroundSession = [NSURLSession sessionWithConfiguration: backgroundConfigObject delegateself delegateQueue: [NSOperationQueue mainQueue]];  
  4.     self.ephemeralSession = [NSURLSession sessionWithConfiguration: ephemeralConfigObject delegateself delegateQueue: [NSOperationQueue mainQueue]];  

       NSURLSession API通過代理來實現異步URL內容存取,代理可以是系統提供的代理,還可以是應用提供的特定代理對象。任務對象當從服務器接收到數據或傳輸完成時調用這些代理對象的方法。

     在創建會話指定相應的代理對象。

3、爲會話添加任務;

使用如下方法來添加數據任務到一個會話。

dataTaskWithURL(_:)

dataTaskWithURL(_:completionHandler:)

dataTaskWithRequest(_:) 

dataTaskWithRequest(_:completionHandler:) 

使用如下方法來添加下載任務到一個會話。

downloadTaskWithURL(_:)

downloadTaskWithURL(_:completionHandler:) 

downloadTaskWithRequest(_:) 

downloadTaskWithRequest(_:completionHandler:) 

downloadTaskWithResumeData(_:) 

downloadTaskWithResumeData(_:completionHandler:) 


使用如下方法來添加上傳任務到一個會話

uploadTaskWithRequest(_:fromData:)

uploadTaskWithRequest(_:fromData:completionHandler:) 

uploadTaskWithRequest(_:fromFile:)

uploadTaskWithRequest(_:fromFile:completionHandler:)

uploadTaskWithStreamedRequest(_:) 

         如下是數據任務創建代碼片段:     

  1. NSURL *url = [NSURL URLWithString@"http://www.example.com/"];  
  2.   
  3. NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithURL: url];  
  4.   
  5. [dataTask resume];  

       下面是下載任務創建代碼片段:         

  1. NSURL *url = [NSURL URLWithString@"https://developer.apple.com/library/ios/documentation/Cocoa/Reference/"  
  2.   
  3.               "Foundation/ObjC_classic/FoundationObjC.pdf"];  
  4.   
  5. NSURLSessionDownloadTask *downloadTask = [self.backgroundSession downloadTaskWithURL: url];  
  6.   
  7. [downloadTask resume];  

4、使用代理方法接收數據及狀態信息


會話的數據任務在使用應用特定代理接收數據時必須實現如下兩個代理方法:

URLSession:dataTask:didReceiveData: 

一次一片的提供請求的數據給會話任務。

URLSession:task:didCompleteWithError:

指示請求數據已經全部接收。


會話的下載任務在下載文件時應該實現如下代理方法:

URLSession:downloadTask:didFinishDownloadingToURL:

下載內容存儲到一個URL指定的一個臨時文件,在該方法返回之前,必須把臨時文件的內容移到一個永久位置,而臨時文件被刪除。

URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: 

爲應用提供關於當前下載進度的狀態信息

URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:

告訴應用已經從從先前的失敗下載中恢復。

URLSession:task:didCompleteWithError: 

告訴應用下載已經失敗。


下載失敗恢復處理:

在應用使用cancelByProducingResumeData: 方法取消下載任務時,可以使用downloadTaskWithResumeData: 或 downloadTaskWithResumeData:completionHandler:方法重新創建一個新下載任務並傳送cancelByProducingResumeData:產生的恢復數據從而接着繼續下載。

在傳輸失敗時,如果任務可恢復,則調用URLSession:task:didCompleteWithError: 方法。在傳送給URLSession:task:didCompleteWithError: 方法的參數NSError中的 userInfo 詞典中包含鍵值爲NSURLSessionDownloadTaskResumeData的恢復數據,因此可以使用downloadTaskWithResumeData: 或 downloadTaskWithResumeData:completionHandler:方法重新創建一個新下載任務來接着恢復數據繼續下載。


系統代理僅能支持基本的URL資源存取任務,不支持認證和後臺下載,並且還必須提供一個completion handler block來把返回的URL數據提交到應用。如下是一個使用系統代理的代碼例子:         

  1. NSURLSession *delegateFreeSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];  
  2.   
  3. [delegateFreeSession dataTaskWithURL: [NSURL URLWithString@"http://www.example.com/"]  
  4.   
  5.                   completionHandler:^(NSData *data, NSURLResponse *response,  
  6.   
  7.                                       NSError *error) {  
  8.   
  9.                       NSLog(@"Got response %@ with error %@.\n", response, error);  
  10.   
  11.                       NSLog(@"DATA:\n%@\nEND DATA\n",  
  12.   
  13.                             [[NSString alloc] initWithData: data  
  14.   
  15.                                     encoding: NSUTF8StringEncoding]);  
  16.   
  17.                   }] resume];  

          會話的上傳任務的任務創建和相關代理方法:

          會話的上傳任務使用HTTP  POST方法來上傳數據。可以以一個NSData對象、一個文件或使用一個流爲HTTP POST請求的body提供內容。

          在以NSData對象提供上傳數據時,應用調用uploadTaskWithRequest:fromData: 或uploadTaskWithRequest:fromData:completionHandler: 方法來創建上傳任務。

在以文件形式提供上傳數據時,應用調用uploadTaskWithRequest:fromFile: 或 uploadTaskWithRequest:fromFile:completionHandler:方法來創建上傳任務

在以流方式提供上傳數據時,應用調用uploadTaskWithStreamedRequest:方法來創建上傳任務。

應用特定代理可以通過實現URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:方法來獲得上傳進度信息。


原文出處:http://blog.csdn.net/goohong/article/details/40505291


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章