[深入浅出Cocoa]iOS网络编程之NSStream
2019獨角獸企業重金招聘Python工程師標準>>>
?
[深入淺出Cocoa]iOS網絡編程之NSStream
目錄(?)[+]
[深入淺出Cocoa]iOS網絡編程之NSStream
羅朝輝 (http://blog.csdn.net/kesalin/)
本文遵循“署名-非商業用途-保持一致”創作公用協議
?
一,NSStream簡介
首先來回顧下。在前文《[深入淺出Cocoa]iOS網絡編程之Socket》中,提到iOS網絡編程層次模型分為三層:
Cocoa層:NSURL,Bonjour,Game Kit,WebKit
Core Foundation層:基于 C 的?CFNetwork 和 CFNetServices
OS層:基于 C 的 BSD socket
前文《iOS網絡編程之Socket》?和《iOS網絡編程之CFNetwork》?講了最底層的 socket 和Core Foundation層的 CFNetwork,本文將介紹位于 Cocoa 中的 NSStream。NSStream 其實只是用 Objective-C 對 CFNetwork 的簡單封裝,它使用名為?NSStreamDelegate 的協議來實現 CFNetwork 中的回調函數的作用,同樣,runloop 也與 NSStream 結合的很好。NSStream 有兩個實體類:NSInputStream 和 NSOutputStream,分別對應 CFNetwork 中的?CFReadStream 和?CFWriteStream。
?
本文示例代碼請查看:
https://github.com/kesalin/iOSSnippet/tree/master/KSNetworkDemo
二,NSStream 類接口簡介
NSStream?類有如下接口:
- (void)open;
- (void)close;
- (id?<NSStreamDelegate>)delegate;
- (void)setDelegate:(id?<NSStreamDelegate>)delegate;
- (void)scheduleInRunLoop:(NSRunLoop?*)aRunLoop forMode:(NSString?*)mode;
- (void)removeFromRunLoop:(NSRunLoop?*)aRunLoop forMode:(NSString?*)mode;
- (NSStreamStatus)streamStatus;
- (NSError?*)streamError;
NSStream 的一些接口與 CFNetwork 類似,如打開,關閉,獲取狀態和錯誤信息,以及和 runloop 結合等在這里就不再重復了。前面提到 NSStream 是通過 NSStreamDelegate 來實現 CFNetwork 中的回調函數,這個可選的協議只有一個接口:
- (void)stream:(NSStream?*)aStream handleEvent:(NSStreamEvent)eventCode;
NSStreamEvent 是一個流事件枚舉:
typedef?NS_OPTIONS(NSUInteger, NSStreamEvent) {
? ? NSStreamEventNone =?0,
? ? NSStreamEventOpenCompleted =?1UL <<?0,
? ? NSStreamEventHasBytesAvailable =?1UL <<?1,
? ? NSStreamEventHasSpaceAvailable =?1UL <<?2,
? ? NSStreamEventErrorOccurred =?1UL <<?3,
? ? NSStreamEventEndEncountered =?1UL <<?4
};
這些事件枚舉的含義也和 CFNetwork 中的?CFStreamEventType 類似,在此也就不再重復了。
NSInputStream?類有如下接口:
- (NSInteger)read:(uint8_t?*)buffer maxLength:(NSUInteger)len;
從流中讀取數據到 buffer 中,buffer 的長度不應少于 len,該接口返回實際讀取的數據長度(該長度最大為 len)。
- (BOOL)getBuffer:(uint8_t?**)buffer length:(NSUInteger?*)len;
獲取當前流中的數據以及大小,注意 buffer 只在下一個流操作之前有效。
- (BOOL)hasBytesAvailable;
檢查流中是否還有數據。
NSOutputStream?類有如下接口:
- (NSInteger)write:(const?uint8_t?*)buffer maxLength:(NSUInteger)len;
將 buffer 中的數據寫入流中,返回實際寫入的字節數。
- (BOOL)hasSpaceAvailable;
檢查流中是否還有可供寫入的空間。
從這些接口可以看出,NSStream 真的就是 CFNetwork 上的一層簡單的 Objective-C 封裝。但 iOS 中的 NSStream 不支持 NShost,這是一個缺陷,蘋果也意識到這問題了(http://developer.apple.com/library/ios/#qa/qa1652/_index.html),我們可以通過 NSStream 的擴展函數來實現該功能:
@implementation?NSStream(StreamsToHost)+?(void)getStreamsToHostNamed:(NSString?*)hostName?????????????????????????port:(NSInteger)port??????????????????inputStream:(out?NSInputStream?**)inputStreamPtr?????????????????outputStream:(out?NSOutputStream?**)outputStreamPtr {CFReadStreamRef?????readStream;CFWriteStreamRef????writeStream;assert(hostName?!=?nil);assert(?(port?>?0)?&&?(port?<?65536)?);assert(?(inputStreamPtr?!=?NULL)?||?(outputStreamPtr?!=?NULL)?);readStream?=?NULL;writeStream?=?NULL;CFStreamCreatePairWithSocketToHost(NULL,(__bridge?CFStringRef)?hostName,port,((inputStreamPtr??!=?NULL)???&readStream?:?NULL),((outputStreamPtr?!=?NULL)???&writeStream?:?NULL));????if?(inputStreamPtr?!=?NULL)?{*inputStreamPtr??=?CFBridgingRelease(readStream);}????if?(outputStreamPtr?!=?NULL)?{*outputStreamPtr?=?CFBridgingRelease(writeStream);} }@end三,客戶端示例代碼
與前面的示例類似,在這里我只演示客戶端示例。同樣,我們也在一個后臺線程中啟動網絡操作:
????NSURL?*?url?=?[NSURL?URLWithString:[NSString?stringWithFormat:@"%@:%@",?serverHost,?serverPort]];NSThread?*?backgroundThread?=?[[NSThread?alloc]?initWithTarget:selfselector:@selector(loadDataFromServerWithURL:)????????????????????????????????????????????????????????????object:url];[backgroundThread?start];然后在?loadDataFromServerWithURL 中創建 NSInputStream,并設置其 delegate,將其加入到 run-loop 的事件源中,然后打開流,運行 runloop:
-?(void)loadDataFromServerWithURL:(NSURL?*)url {NSInputStream?*?readStream;[NSStream?getStreamsToHostNamed:[url?host]port:[[url?port]?integerValue]inputStream:&readStreamoutputStream:NULL];[readStream?setDelegate:self];[readStream?scheduleInRunLoop:[NSRunLoop?currentRunLoop]?forMode:NSDefaultRunLoopMode];[readStream?open];[[NSRunLoop?currentRunLoop]?run]; }因為我們將 KSNSStreamViewController 當作 NSInputStream 的 delegate,因此要在 KSNSStreamViewController 中實現該 delgate:
#pragma?mark?NSStreamDelegate-?(void)stream:(NSStream?*)stream?handleEvent:(NSStreamEvent)eventCode {NSLog(@"?>>?NSStreamDelegate?in?Thread?%@",?[NSThread?currentThread]);????switch?(eventCode)?{????????case?NSStreamEventHasBytesAvailable:?{????????????if?(_receivedData?==?nil)?{_receivedData?=?[[NSMutableData?alloc]?init];}uint8_t?buf[kBufferSize];????????????int?numBytesRead?=?[(NSInputStream?*)stream?read:buf?maxLength:kBufferSize];????????????if?(numBytesRead?>?0)?{[self?didReceiveData:[NSData?dataWithBytes:buf?length:numBytesRead]];}?else?if?(numBytesRead?==?0)?{NSLog(@"?>>?End?of?stream?reached");}?else?{NSLog(@"?>>?Read?error?occurred");}????????????break;}????????????case?NSStreamEventErrorOccurred:?{NSError?*?error?=?[stream?streamError];NSString?*?errorInfo?=?[NSString?stringWithFormat:@"Failed?while?reading?stream;?error?'%@'?(code?%d)",?error.localizedDescription,?error.code];[self?cleanUpStream:stream];[self?networkFailedWithErrorMessage:errorInfo];}????????????case?NSStreamEventEndEncountered:?{[self?cleanUpStream:stream];[self?didFinishReceivingData];????????????break;}????????????default:????????????break;} }當數據讀取完畢或者讀取失敗時,調用 cleanUpStream 方法來關閉流:
-?(void)cleanUpStream:(NSStream?*)stream {[stream?removeFromRunLoop:[NSRunLoop?currentRunLoop]?forMode:NSDefaultRunLoopMode];[stream?close];stream?=?nil; }四,結語
通過上面的示例演示,我們可以看到 NSStream 只是用 Objective-C?對 CFNetwork 的一層簡單封裝,但確實大大方便了我們使用 socket 進行編程,因此在大多數情況下,我們都應該優先使用 NSStream 進行 socket 編程。
轉載于:https://my.oschina.net/u/1984662/blog/312114
總結
以上是生活随笔為你收集整理的[深入浅出Cocoa]iOS网络编程之NSStream的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 4k对齐是2048还是4096
- 下一篇: shell的控制语句