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

本例源于关东升同名书籍,有兴趣可以去买一本,我手头就一本,初看,还不错:
所有网络编程步骤都大同小异:

服务器的工作流程:

1.    服务器调用 socket(...) 创建socket; 
2.    服务器调用 listen(...) 设置缓冲区; 
3.    服务器通过 accept(...)接受客户端请求建立连接; 
4.    服务器与客户端建立连接之后,就可以通过 send(...)/receive(...)向客户端发送或从客户端接收数据; 
5.    服务器调用 close 关闭 socket; 

客户端工作流程:  

1.    客户端调用 socket(...) 创建socket; 
2.    客户端调用 connect(...) 向服务器发起连接请求以建立连接; 
3.    客户端与服务器建立连接之后,就可以通过 send(...)/receive(...)向客户端发送或从客户端接收数据; 
4.    客户端调用 close 关闭 socket;

由于 iOS 设备通常是作为客户端,下文将演示如何编写客户端代码。

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

有关客户端应用的UI部分不再介绍了,我们直接看代码部分,Socket客户端可以采用CFStreamNSStream实现,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 
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

 

 

定义属性inputStreamoutputStream,它们输入流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 }

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

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

 

它们都调用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 }

 

iOS网络编程实践--NSStream实现TCP Socket iPhone客户端,,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。