iOS網絡編程實踐--NSStream實現TCP Socket iPhone客戶端
客戶端我們使用iPhone應用程序,畫麵比較簡單。點擊發送按鈕,給服務器發送一些字符串過去。點擊接收按鈕就會從服務器讀取一些字符串,並且顯示在畫麵上。
有關客戶端應用的UI部分不再介紹了,我們直接看代碼部分,Socket客戶端可以采用CFStream或NSStream實現。為了給讀者介紹更多的知識,本例我們采用NSStream實現。NSStream實現采用Objective-C語言,一些麵向對象的類。
下麵我們看看客戶端視圖控製器ViewController.h
#import <CoreFoundation/CoreFoundation.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 9000 @interface ViewController : UIViewController<NSStreamDelegate> { int flag ; //操作標誌 0為發送 1為接收 } @property (nonatomic, retain) NSInputStream *inputStream; @property (nonatomic, retain) NSOutputStream *outputStream; @property (weak, nonatomic) IBOutlet UILabel *message; - (IBAction)sendData:(id)sender; - (IBAction)receiveData:(id)sender; @end
定義屬性inputStream和outputStream,它們輸入流NSInputStream和輸出流NSOutputStream類。它們與服務器CFStream實現中的輸入流CFReadStreamRef和輸出流CFWriteStreamRef對應的。
視圖控製器ViewController.m的初始化網絡方法initNetworkCommunication代碼:
- (void)initNetworkCommunication { CFReadStreamRef readStream; CFWriteStreamRef writeStream; CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@”192.168.1.103″, PORT, &readStream, &writeStream); ① _inputStream = (__bridge_transfer NSInputStream *)readStream; ② _outputStream = (__bridge_transfer NSOutputStream*)writeStream; ③ [_inputStream setDelegate:self]; ④ [_outputStream setDelegate:self]; ⑤ [_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; ⑥ [_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; ⑦ [_inputStream open]; ⑧ [_outputStream open]; ⑨ } 點擊發送和接收按鈕觸發的方法如下: /* 點擊發送按鈕 */ - (IBAction)sendData:(id)sender { flag = 0; [self initNetworkCommunication]; } /* 點擊接收按鈕 */ - (IBAction)receiveData:(id)sender { flag = 1; [self initNetworkCommunication]; }
它們都調用initNetworkCommunication方法,並設置操作標識flag,如果flag為0發送數據,flag為1接收數據。
流的狀態的變化觸發很多事件,並回調NSStreamDelegate協議中定義的方法stream:handleEvent:,其代碼如下:
-(void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent { NSString *event; switch (streamEvent) { case NSStreamEventNone: event = @”NSStreamEventNone”; break; case NSStreamEventOpenCompleted: event = @”NSStreamEventOpenCompleted”; break; case NSStreamEventHasBytesAvailable: event = @”NSStreamEventHasBytesAvailable”; if (flag ==1 && theStream == _inputStream) { NSMutableData *input = [[NSMutableData alloc] init]; uint8_t buffer[1024]; ① int len; while([_inputStream hasBytesAvailable]) ② { len = [_inputStream read:buffer maxLength:sizeof(buffer)]; ③ if (len > 0) { [input appendBytes:buffer length:len]; } } NSString *resultstring = [[NSString alloc] initWithData:input encoding:NSUTF8StringEncoding]; NSLog(@”接收:%@”,resultstring); _message.text = resultstring; } break; case NSStreamEventHasSpaceAvailable: event = @”NSStreamEventHasSpaceAvailable”; if (flag ==0 && theStream == _outputStream) { //輸出 UInt8 buff[] = ”Hello Server!”; ④ [_outputStream write:buff maxLength: strlen((const char*)buff)+1]; ⑤ //關閉輸出流 [_outputStream close]; } break; case NSStreamEventErrorOccurred: event = @”NSStreamEventErrorOccurred”; [self close]; ⑥ break; case NSStreamEventEndEncountered: event = @”NSStreamEventEndEncountered”; NSLog(@”Error:%d:%@”,[[theStream streamError] code], [[theStream streamError] localizedDescription]); break; default: [self close]; ⑦ event = @”Unknown”; break; } NSLog(@”event——%@”,event); }
在讀取數據分支(NSStreamEventHasBytesAvailable)中,代碼第①行為讀取數據準備緩衝區,本例中設置的是1024個字節,這個大小會對流的讀取有很多的影響。第②行代碼使用hasBytesAvailable方法判斷是否流有數據可以讀,如果有可讀數據就進行循環讀取。第③行代碼使用流的read:maxLength:方法讀取數據到緩衝區,第1個參數是緩衝區對象buffer,第2個參數是讀取的緩衝區的字節長度。
在寫入數據分支(NSStreamEventHasSpaceAvailable)中,代碼第④行是要寫入的數據,第⑤行代碼[_outputStream write:buff maxLength: strlen((const char*)buff)+1]是寫如數據方法。
第⑥和第⑦行代碼[self close]調用close方法關閉,close方法代碼如下:
-(void)close { [_outputStream close]; [_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [_outputStream setDelegate:nil]; [_inputStream close]; [_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [_inputStream setDelegate:nil]; }
最後更新:2017-04-03 20:51:32