现在的位置: 首页 > 综合 > 正文

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

2018年07月28日 ⁄ 综合 ⁄ 共 6886字 ⁄ 字号 评论关闭

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

2

有关客户端应用的UI部分不再介绍了,我们直接看代码部分,Socket客户端可以采用CFStream或NSStream实现。为了给读者介绍更多的知识,本例我们采用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.   
  17. int flag ; //操作标志 0为发送 1为接收  
  18.   
  19. }  
  20.   
  21.    
  22.   
  23. @property (nonatomic, retain) NSInputStream *inputStream;  
  24.   
  25. @property (nonatomic, retain) NSOutputStream *outputStream;  
  26.   
  27.    
  28.   
  29. @property (weak, nonatomic) IBOutlet UILabel *message;  
  30.   
  31.    
  32.   
  33. - (IBAction)sendData:(id)sender;  
  34.   
  35. - (IBAction)receiveData:(id)sender;  
  36.   
  37.    
  38.   
  39. @end  

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

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

  1. - (void)initNetworkCommunication  
  2.   
  3. {  
  4.   
  5. CFReadStreamRef readStream;  
  6.   
  7. CFWriteStreamRef writeStream;  
  8.   
  9. CFStreamCreatePairWithSocketToHost(NULL,  
  10.   
  11. (CFStringRef)@”192.168.1.103″, PORT, &readStream, &writeStream);   ①  
  12.   
  13. _inputStream = (__bridge_transfer NSInputStream *)readStream; ②  
  14.   
  15. _outputStream = (__bridge_transfer NSOutputStream*)writeStream;  ③  
  16.   
  17. [_inputStream setDelegate:self];  ④  
  18.   
  19. [_outputStream setDelegate:self];  ⑤  
  20.   
  21. [_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]  
  22.   
  23. forMode:NSDefaultRunLoopMode]; ⑥  
  24.   
  25. [_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]  
  26.   
  27. forMode:NSDefaultRunLoopMode];  ⑦  
  28.   
  29. [_inputStream open];  ⑧  
  30.   
  31. [_outputStream open];  ⑨  
  32.   
  33. }  
  34.   
  35. 点击发送和接收按钮触发的方法如下:  
  36.   
  37. /* 点击发送按钮  */  
  38.   
  39. - (IBAction)sendData:(id)sender {  
  40.   
  41. flag = 0;  
  42.   
  43. [self initNetworkCommunication];  
  44.   
  45. }  
  46.   
  47. /* 点击接收按钮  */  
  48.   
  49. - (IBAction)receiveData:(id)sender {  
  50.   
  51. flag = 1;  
  52.   
  53. [self initNetworkCommunication];  
  54.   
  55. }  

它们都调用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.   
  35. len = [_inputStream read:buffer maxLength:sizeof(buffer)];  ③  
  36.   
  37. if (len > 0)  
  38.   
  39. {  
  40.   
  41. [input appendBytes:buffer length:len];  
  42.   
  43. }  
  44.   
  45. }  
  46.   
  47. NSString *resultstring = [[NSString alloc]  
  48.   
  49. initWithData:input encoding:NSUTF8StringEncoding];  
  50.   
  51. NSLog(@”接收:%@”,resultstring);  
  52.   
  53. _message.text = resultstring;  
  54.   
  55. }  
  56.   
  57. break;  
  58.   
  59. case NSStreamEventHasSpaceAvailable:  
  60.   
  61. event = @”NSStreamEventHasSpaceAvailable”;  
  62.   
  63. if (flag ==0 && theStream == _outputStream) {  
  64.   
  65. //输出  
  66.   
  67. UInt8 buff[] = ”Hello Server!”; ④  
  68.   
  69. [_outputStream write:buff maxLength: strlen((const char*)buff)+1]; ⑤  
  70.   
  71. //关闭输出流  
  72.   
  73. [_outputStream close];  
  74.   
  75. }  
  76.   
  77. break;  
  78.   
  79. case NSStreamEventErrorOccurred:  
  80.   
  81. event = @”NSStreamEventErrorOccurred”;  
  82.   
  83. [self close]; ⑥  
  84.   
  85. break;  
  86.   
  87. case NSStreamEventEndEncountered:  
  88.   
  89. event = @”NSStreamEventEndEncountered”;  
  90.   
  91. NSLog(@”Error:%d:%@”,[[theStream streamError] code],  
  92.   
  93. [[theStream streamError] localizedDescription]);  
  94.   
  95. break;  
  96.   
  97. default:  
  98.   
  99. [self close];  ⑦  
  100.   
  101. event = @”Unknown”;  
  102.   
  103. break;  
  104.   
  105. }  
  106.   
  107. NSLog(@”event——%@”,event);  
  108.   
  109. }  

在读取数据分支(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.   
  5. [_outputStream close];  
  6.   
  7. [_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop]  
  8.   
  9. forMode:NSDefaultRunLoopMode];  
  10.   
  11. [_outputStream setDelegate:nil];  
  12.   
  13. [_inputStream close];  
  14.   
  15. [_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop]  
  16.   
  17. forMode:NSDefaultRunLoopMode];  
  18.   
  19. [_inputStream setDelegate:nil];  
  20.   
  21. }  

抱歉!评论已关闭.