<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [深入淺出Cocoa]iOS網絡編程之CFNetwork

      [深入淺出Cocoa]iOS網絡編程之CFNetwork

      羅朝輝 (http://www.rzrgm.cn/kesalin/)

      本文遵循“署名-非商業用途-保持一致”創作公用協議
       

      一,CFNetwork 簡介

      首先來回顧下。在前文《[深入淺出Cocoa]iOS網絡編程之Socket》中,提到iOS網絡編程層次模型分為三層:
      • Cocoa層:NSURL,Bonjour,Game Kit,WebKit
      • Core Foundation層:基于 C 的 CFNetwork 和 CFNetServices
      • OS層:基于 C 的 BSD socket
      前文講的是最底層的 socket,本文將介紹位于 Core Foundation 中的 CFNetwork。CFNetwork 只是對 BSD socket 的進行了輕量級的封裝,但在 iOS 中使用 CFNetwork 有一個顯著的好處,那就是 CFNetwork 與系統級別的設置(如:天線設置)以及 run-loop 結合得很好。每一個線程都有自己的 run-loop,因此我們可以 CFNetwork 當中事件源加入到 run-loop 中,這樣就可以在線程的 run-loop 中處理網絡事件了。BTW,大名鼎鼎的 ASIHttpRequest 庫就是基于 CFNetwork 封裝的。
       
      本文示例代碼就是這樣做的,源碼請查看:
       

      二,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 在使用之前就如原生 socket 一樣,必須顯式地調用其 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);

       

      三,客戶端示例代碼

      與 socket 演示類似,在這里我只演示客戶端示例。同樣,我們也在一個后臺線程中啟動網絡操作:

          NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:@"%@:%@", serverHost, serverPort]];
          NSThread * backgroundThread = [[NSThread alloc] initWithTarget:self
                                                                selector:@selector(loadDataFromServerWithURL:)
                                                                  object:url];
          [backgroundThread start];

      然后在 loadDataFromServerWithURL 中創建 socket 流,并設置其回調函數,將其加入到 run-loop 的事件源中,然后啟動之:

      - (void)loadDataFromServerWithURL:(NSURL *)url
      {
          NSString * host = [url host];
          NSInteger port = [[url port] integerValue];
          
          // Keep a reference to self to use for controller callbacks
          //
          CFStreamClientContext ctx = {0, (__bridge void *)(self), NULL, NULL, NULL};
          
          // Get callbacks for stream data, stream end, and any errors
          //
          CFOptionFlags registeredEvents = (kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred);
          
          // Create a read-only socket
          //
          CFReadStreamRef readStream;
          CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)host, port, &readStream, NULL);
          
          // Schedule the stream on the run loop to enable callbacks
          //
          if (CFReadStreamSetClient(readStream, registeredEvents, socketCallback, &ctx)) {
              CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
              
          }
          else {
              [self networkFailedWithErrorMessage:@"Failed to assign callback method"];
              return;
          }
          
          // Open the stream for reading
          //
          if (CFReadStreamOpen(readStream) == NO) {
              [self networkFailedWithErrorMessage:@"Failed to open read stream"];
              
              return;
          }
          
          CFErrorRef error = CFReadStreamCopyError(readStream);
          if (error != NULL) {
              if (CFErrorGetCode(error) != 0) {
                  NSString * errorInfo = [NSString stringWithFormat:@"Failed to connect stream; error '%@' (code %ld)", (__bridge NSString*)CFErrorGetDomain(error), CFErrorGetCode(error)];
                  [self networkFailedWithErrorMessage:errorInfo];
              }
              
              CFRelease(error);
              
              return;
          }
          
          NSLog(@"Successfully connected to %@", url);
          
          // Start processing
          //
          CFRunLoopRun();
      }

      參考前面的接口說明,相信你不難理解上面的代碼。前面唯一沒有提到的接口就是 CFReadStreamCopyError,該接口用于獲取當前的錯誤信息,如果沒有錯誤則返回 NULL。

      CFErrorRef CFReadStreamCopyError(CFReadStreamRef stream);
      
      CFErrorRef CFWriteStreamCopyError(CFWriteStreamRef stream);

      此外,我們還可以調用如下接口獲取 socket stream 的當前狀態:

      CFStreamStatus CFReadStreamGetStatus(CFReadStreamRef stream);
      
      CFStreamStatus CFWriteStreamGetStatus(CFWriteStreamRef stream);

      在上面的代碼中,我們設置了當有數據可以讀取,流到達結尾處時以及錯誤發生時調用回調函數 socketCallback:

      void socketCallback(CFReadStreamRef stream, CFStreamEventType event, void * myPtr)
      {
          KSCFNetworkViewController * controller = (__bridge KSCFNetworkViewController *)myPtr;
          
          switch(event) {
              case kCFStreamEventHasBytesAvailable: {
                  // Read bytes until there are no more
                  //
                  while (CFReadStreamHasBytesAvailable(stream)) {
                      UInt8 buffer[kBufferSize];
                      int numBytesRead = CFReadStreamRead(stream, buffer, kBufferSize);
                      
                      [controller didReceiveData:[NSData dataWithBytes:buffer length:numBytesRead]];
                  }
                  
                  break;
              }
                  
              case kCFStreamEventErrorOccurred: {
                  CFErrorRef error = CFReadStreamCopyError(stream);
                  if (error != NULL) {
                      if (CFErrorGetCode(error) != 0) {
                          NSString * errorInfo = [NSString stringWithFormat:@"Failed while reading stream; error '%@' (code %ld)", (__bridge NSString*)CFErrorGetDomain(error), CFErrorGetCode(error)];
                          
                          [controller networkFailedWithErrorMessage:errorInfo];
                      }
                      
                      CFRelease(error);
                  }
                  
                  break;
              }
                  
              case kCFStreamEventEndEncountered:
                  // Finnish receiveing data
                  //
                  [controller didFinishReceivingData];
                  
                  // Clean up
                  //
                  CFReadStreamClose(stream);
                  CFReadStreamUnscheduleFromRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
                  CFRunLoopStop(CFRunLoopGetCurrent());
                  
                  break;
                  
              default:
                  break;
          }
      }

      上面的代碼也很好理解,當有數據可以讀取時,讀取之,然后更新 UI;當流到達結尾處時,關閉流,執行清理工作;當錯誤發送時,報告錯誤信息。

       

      四,擴展

      雖然上面的代碼只演示了如何使用 CFNetwork 的 CFReadStream 來讀取數據,寫入數據使用 CFWriteStream,其工作流程也是一樣的。在這里就不再介紹了。

        

      posted @ 2013-04-14 20:58  飄飄白云  閱讀(13178)  評論(1)    收藏  舉報
      本博客遵循 Creative Commons License “署名-非商業用途-保持一致”創作共用協議。 與我聯系
      主站蜘蛛池模板: 国产精品一区二区小视频| 国产精自产拍久久久久久蜜| 久久久久久伊人高潮影院| 亚洲丰满熟女一区二区蜜桃| 久久综合给合久久狠狠97色 | 久久久久国产精品熟女影院| 99久久免费只有精品国产| 亚洲免费人成在线视频观看| 狠狠色狠狠色综合日日不卡| 2018年亚洲欧美在线v| 天天爱天天做天天爽夜夜揉| 日本一码二码三码的区分| 中国熟妇毛多多裸交视频| 特级欧美AAAAAAA免费观看| 国产老熟女伦老熟妇露脸| 国语精品一区二区三区| 亚洲av一本二本三本| 免费看又黄又无码的网站| 亚洲成亚洲成网中文字幕| 日韩精品国产中文字幕| 国产对白熟女受不了了| 亚洲性一交一乱一伦视频| 国产午夜精品理论大片| 国产精品天干天干综合网| 丁青县| 久久99国产精品尤物| 高清偷拍一区二区三区| 日本韩国日韩少妇熟女少妇| 久九九精品免费视频| 国产日韩精品中文字幕| 亚洲国产亚洲国产路线久久| 男女动态无遮挡动态图| 德清县| 国产视色精品亚洲一区二区| 99亚洲男女激情在线观看| 国产在线午夜不卡精品影院| 亚洲熟妇熟女久久精品综合| 黑人玩弄人妻中文在线| 国产老熟女乱子一区二区| 东方av四虎在线观看| 高清自拍亚洲精品二区|