客户端我们使用iPhone应用程序,画面比较简单。点击发送按钮,给服务器发送一些字符串过去。点击接收按钮就会从服务器读取一些字符串,并且显示在画面上。

 

iOS网络编程实践--NSStream实现TCP Socket iPhone客户端_iOS TCP编程

有关客户端应用的UI部分不再介绍了,我们直接看代码部分,Socket客户端可以采用CFStream或NSStream实现,CFStream 实现方式与服务器端基本一样。为了给读者介绍更多的知识,本例我们采用NSStream实现。NSStream实现采用Objective-C语言,一些 面向对象的类。

下面我们看看客户端视图控制器ViewController.h

 

  1. #import <CoreFoundation/CoreFoundation.h> 
  2.  
  3. #include <sys/socket.h> 
  4.  
  5. #include <netinet/in.h> 
  6.  
  7.   
  8.  
  9. #define PORT 9000 
  10.  
  11.   
  12.  
  13. @interface ViewController : UIViewController<NSStreamDelegate> 
  14.  
  15.  
  16. int flag ; //操作标志 0为发送 1为接收 
  17.  
  18.  
  19.   
  20.  
  21. @property (nonatomic, retain) NSInputStream *inputStream; 
  22.  
  23. @property (nonatomic, retain) NSOutputStream *outputStream; 
  24.  
  25.   
  26.  
  27. @property (weak, nonatomic) IBOutlet UILabel *message; 
  28.  
  29.   
  30.  
  31. - (IBAction)sendData:(id)sender; 
  32.  
  33. - (IBAction)receiveData:(id)sender; 
  34.  
  35.   
  36.  
  37. @end 

定义属性inputStream和outputStream,它们输入流NSInputStream和输出流NSOutputStream类。它们与服务器CFStream实现中的输入流CFReadStreamRef和输出流CFWriteStreamRef对应的。

视图控制器ViewController.m的初始化网络方法initNetworkCommunication代码:

 

  1. - (void)initNetworkCommunication 
  2.  
  3.  
  4. CFReadStreamRef readStream; 
  5.  
  6. CFWriteStreamRef writeStream; 
  7.  
  8. CFStreamCreatePairWithSocketToHost(NULL, 
  9.  
  10. (CFStringRef)@”192.168.1.103″, PORT, &readStream, &writeStream);   ① 
  11.  
  12. _inputStream = (__bridge_transfer NSInputStream *)readStream; ② 
  13.  
  14. _outputStream = (__bridge_transfer NSOutputStream*)writeStream;  ③ 
  15.  
  16. [_inputStream setDelegate:self];  ④ 
  17.  
  18. [_outputStream setDelegate:self];  ⑤ 
  19.  
  20. [_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] 
  21.  
  22. forMode:NSDefaultRunLoopMode]; ⑥ 
  23.  
  24. [_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] 
  25.  
  26. forMode:NSDefaultRunLoopMode];  ⑦ 
  27.  
  28. [_inputStream open];  ⑧ 
  29.  
  30. [_outputStream open];  ⑨ 
  31.  

点击发送和接收按钮触发的方法如下:

 

  1. /* 点击发送按钮  */ 
  2.  
  3. - (IBAction)sendData:(id)sender { 
  4.  
  5. flag = 0; 
  6.  
  7. [self initNetworkCommunication]; 
  8.  
  9.  
  10. /* 点击接收按钮  */ 
  11.  
  12. - (IBAction)receiveData:(id)sender { 
  13.  
  14. flag = 1; 
  15.  
  16. [self initNetworkCommunication]; 
  17.  

它们都调用initNetworkCommunication方法,并设置操作标识flag,如果flag0发送数据,flag1接收数据。

流的状态的变化触发很多事件,并回调NSStreamDelegate协议中定义的方法stream:handleEvent:,其代码如下:

 

  1. -(void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent { 
  2.  
  3. NSString *event; 
  4.  
  5. switch (streamEvent) { 
  6.  
  7. case NSStreamEventNone: 
  8.  
  9. event = @”NSStreamEventNone”; 
  10.  
  11. break
  12.  
  13. case NSStreamEventOpenCompleted: 
  14.  
  15. event = @”NSStreamEventOpenCompleted”; 
  16.  
  17. break
  18.  
  19. case NSStreamEventHasBytesAvailable: 
  20.  
  21. event = @”NSStreamEventHasBytesAvailable”; 
  22.  
  23. if (flag ==1 && theStream == _inputStream) { 
  24.  
  25. NSMutableData *input = [[NSMutableData alloc] init]; 
  26.  
  27. uint8_t buffer[1024];  ① 
  28.  
  29. int len; 
  30.  
  31. while([_inputStream hasBytesAvailable]) ② 
  32.  
  33.  
  34. len = [_inputStream read:buffer maxLength:sizeof(buffer)];  ③ 
  35.  
  36. if (len > 0) 
  37.  
  38.  
  39. [input appendBytes:buffer length:len]; 
  40.  
  41.  
  42.  
  43. NSString *resultstring = [[NSString alloc] 
  44.  
  45. initWithData:input encoding:NSUTF8StringEncoding]; 
  46.  
  47. NSLog(@”接收:%@”,resultstring); 
  48.  
  49. _message.text = resultstring; 
  50.  
  51.  
  52. break
  53.  
  54. case NSStreamEventHasSpaceAvailable: 
  55.  
  56. event = @”NSStreamEventHasSpaceAvailable”; 
  57.  
  58. if (flag ==0 && theStream == _outputStream) { 
  59.  
  60. //输出 
  61.  
  62. UInt8 buff[] = ”Hello Server!”; ④ 
  63.  
  64. [_outputStream write:buff maxLength: strlen((const char*)buff)+1]; ⑤ 
  65.  
  66. //关闭输出流 
  67.  
  68. [_outputStream close]; 
  69.  
  70.  
  71. break
  72.  
  73. case NSStreamEventErrorOccurred: 
  74.  
  75. event = @”NSStreamEventErrorOccurred”; 
  76.  
  77. [self close]; ⑥ 
  78.  
  79. break
  80.  
  81. case NSStreamEventEndEncountered: 
  82.  
  83. event = @”NSStreamEventEndEncountered”; 
  84.  
  85. NSLog(@”Error:%d:%@”,[[theStream streamError] code], 
  86.  
  87. [[theStream streamError] localizedDescription]); 
  88.  
  89. break
  90.  
  91. default
  92.  
  93. [self close];  ⑦ 
  94.  
  95. event = @”Unknown”; 
  96.  
  97. break
  98.  
  99.  
  100. NSLog(@”event——%@”,event); 
  101.  

在读取数据分支(NSStreamEventHasBytesAvailable)中,代码第①行为读取数据准备缓冲区,本例中设置的是1024个字节,这个大小会对流的读取有很多的影响。第②行代码使用hasBytesAvailable方法判断是否流有数据可以读,如果有可读数据就进行循环读取。第③行代码使用流的read:maxLength:方法读取数据到缓冲区,第1个参数是缓冲区对象buffer,第2个参数是读取的缓冲区的字节长度。

在写入数据分支(NSStreamEventHasSpaceAvailable)中,代码第④行是要写入的数据,第⑤行代码 [_outputStream write:buff maxLength: strlen((const char*)buff)+1]是写如数据方 法。

第⑥和第⑦行代码[self close]调用close方法关闭,close方法代码如下:

 

  1. -(void)close 
  2.  
  3.  
  4. [_outputStream close]; 
  5.  
  6. [_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] 
  7.  
  8. forMode:NSDefaultRunLoopMode]; 
  9.  
  10. [_outputStream setDelegate:nil]; 
  11.  
  12. [_inputStream close]; 
  13.  
  14. [_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] 
  15.  
  16. forMode:NSDefaultRunLoopMode]; 
  17.  
  18. [_inputStream setDelegate:nil]; 
  19.