上一篇實現了簡單的 Server 端程序,服務端通過 void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType, void* clientCallBackInfo) 函數向客戶端發送“你好,客戶端!”的消息,如果客戶端接收成功的話就會顯示“你好,客戶端!”;需要顯示服務端發送過來的消息,那麼就需要定義一個UILabel輸出口(IBOutlet)來顯示消息;客戶端還需要添加兩個按鈕,一個用來接收服務端消息的動作事件方法 receiveData:和發送客戶端“收到啦,服務端”消息的動作事件方法 sendData:;添加關聯就可以了,比較簡單,下面來看一下具體實現的代碼:
ViewController.h
#importViewController.m#import #include #include #define PORT 8734 //將端口設置為8734,可以根據具體情況改變 @interface ViewController : UIViewController { 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
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
//初始化客戶端網絡連接
- (void)initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
//這裡需要填寫 IP 地址,根據自己路由可以用的 IP來定,這裡是192.168.1.103,如果不知道可以下載 IP 掃描器
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"192.168.1.103", PORT, &readStream, &writeStream);
self.inputStream = (__bridge_transfer NSInputStream *)readStream; //將CFStream對象轉化為NSStream對象
self.outputStream = (__bridge_transfer NSOutputStream
*)writeStream; //將CFStream對象轉化為NSStream對象
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode]; //NSStream 方法 scheduleInRunLoop: 設置 Run Loop
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[self.inputStream open]; //NSStream 的 open 方法來打開數據流對象
[self.outputStream open];
}
//關閉數據流操作
-(void)close
{
[self.outputStream close];
[self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream setDelegate:nil];
[self.inputStream close];
[self.inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream setDelegate:nil];
}
//從客戶端發送數據事件方法
- (IBAction)sendData:(id)sender {
flag = 0; //表示發送
[self initNetworkCommunication];
}
//從服務端接收數據事件方法
- (IBAction)receiveData:(id)sender {
flag = 1; //表示接收
[self initNetworkCommunication];
}
-(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[2048]; //讀取數據准備緩沖區,本例中設置的是2048字節,這個大小會對流的讀取有很大影響
int len;
while([self.inputStream hasBytesAvailable])
{
len = [self.inputStream read:buffer maxLength:sizeof(buffer)]; //讀取數據到緩沖區
if (len > 0)
{
[input appendBytes:buffer length:len];
}
}
NSString *resultstring = [[NSString alloc] initWithData:input encoding:NSUTF8StringEncoding];
NSLog(@"接收:%@",resultstring);
self.message.text = resultstring;
}
break;
case NSStreamEventHasSpaceAvailable: //這個流可以接收數據的寫入,在寫數據時使用
event = @"NSStreamEventHasSpaceAvailable";
if (flag ==0 && theStream == _outputStream) {
//輸出
UInt8 buff[] = "收到啦,服務端!"; //向服務端發送的消息
[self.outputStream write:buff maxLength: strlen((const char*)buff)+1]; //向服務端寫入數據方法
//必須關閉輸出流否則,服務器端一直讀取不會停止,
[self.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;
}
}
@end
從代碼可以知道,客戶端采用的是 APPLE 自家的 NSStream 來實現的,都是比較簡單的基本數據流操作。
如有哪些不對的地方,歡迎指出!