客戶端我們使用iPhone應用程序,畫面比較簡單。點擊發送按鈕,給服務器發送一些字符串過去。點擊接收按鈕就會從服務器讀取一些字符串,并且顯示在畫面上。
?
?
有關客戶端應用的UI部分不再介紹了,我們直接看代碼部分,Socket客戶端可以采用CFStream或NSStream實現,CFStream 實現方式與服務器端基本一樣。為了給讀者介紹更多的知識,本例我們采用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];}
?