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

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

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

#import<CoreFoundation/CoreFoundation.h>#include<sys/socket.h>#include<netinet/in.h>#definePORT9000@interfaceViewController:UIViewController<NSStreamDelegate>{intflag;//操作标志0为发送1为接收}@property(nonatomic,retain)NSInputStream*inputStream;@property(nonatomic,retain)NSOutputStream*outputStream;@property(weak,nonatomic)IBOutletUILabel*message;-(IBAction)sendData:(id)sender;-(IBAction)receiveData:(id)sender;@end

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

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

-(void)initNetworkCommunication{CFReadStreamRefreadStream;CFWriteStreamRefwriteStream;CFStreamCreatePairWithSocketToHost(NULL,(CFStringRef)@”192.168.1.103″,PORT,&readStream,&writeStream);①_inputStream=(__bridge_transferNSInputStream*)readStream;②_outputStream=(__bridge_transferNSOutputStream*)writeStream;③[_inputStreamsetDelegate:self];④[_outputStreamsetDelegate:self];⑤[_inputStreamscheduleInRunLoop:[NSRunLoopcurrentRunLoop]forMode:NSDefaultRunLoopMode];⑥[_outputStreamscheduleInRunLoop:[NSRunLoopcurrentRunLoop]forMode:NSDefaultRunLoopMode];⑦[_inputStreamopen];⑧[_outputStreamopen];⑨}

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

/*点击发送按钮*/-(IBAction)sendData:(id)sender{flag=0;[selfinitNetworkCommunication];}/*点击接收按钮*/-(IBAction)receiveData:(id)sender{flag=1;[selfinitNetworkCommunication];}

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

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

-(void)stream:(NSStream*)theStreamhandleEvent:(NSStreamEvent)streamEvent{NSString*event;switch(streamEvent){caseNSStreamEventNone:event=@”NSStreamEventNone”;break;caseNSStreamEventOpenCompleted:event=@”NSStreamEventOpenCompleted”;break;caseNSStreamEventHasBytesAvailable:event=@”NSStreamEventHasBytesAvailable”;if(flag==1&&theStream==_inputStream){NSMutableData*input=[[NSMutableDataalloc]init];uint8_tbuffer[1024];①intlen;while([_inputStreamhasBytesAvailable])②{len=[_inputStreamread:buffermaxLength:sizeof(buffer)];③if(len>0){[inputappendBytes:bufferlength:len];}}NSString*resultstring=[[NSStringalloc]initWithData:inputencoding:NSUTF8StringEncoding];NSLog(@”接收:%@”,resultstring);_message.text=resultstring;}break;caseNSStreamEventHasSpaceAvailable:event=@”NSStreamEventHasSpaceAvailable”;if(flag==0&&theStream==_outputStream){//输出UInt8buff[]=”HelloServer!”;④[_outputStreamwrite:buffmaxLength:strlen((constchar*)buff)+1];⑤//关闭输出流[_outputStreamclose];}break;caseNSStreamEventErrorOccurred:event=@”NSStreamEventErrorOccurred”;[selfclose];⑥break;caseNSStreamEventEndEncountered:event=@”NSStreamEventEndEncountered”;NSLog(@”Error:%d:%@”,[[theStreamstreamError]code],[[theStreamstreamError]localizedDescription]);break;default:[selfclose];⑦event=@”Unknown”;break;}NSLog(@”event——%@”,event);}

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

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

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

-(void)close{[_outputStreamclose];[_outputStreamremoveFromRunLoop:[NSRunLoopcurrentRunLoop]forMode:NSDefaultRunLoopMode];[_outputStreamsetDelegate:nil];[_inputStreamclose];[_inputStreamremoveFromRunLoop:[NSRunLoopcurrentRunLoop]forMode:NSDefaultRunLoopMode];[_inputStreamsetDelegate:nil];}