iOS 中基於Socket 的 C/S 結構的網絡通信(下) – iPhone手機開發技術文章 iPhone軟體開發教學課程

上一篇實現瞭簡單的 Server 端程序,服務端通過 void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType, void* clientCallBackInfo) 函數向客戶端發送“你好,客戶端!”的消息,如果客戶端接收成功的話就會顯示“你好,客戶端!”;需要顯示服務端發送過來的消息,那麼就需要定義一個UILabel輸出口(IBOutlet)來顯示消息;客戶端還需要添加兩個按鈕,一個用來接收服務端消息的動作事件方法
receiveData:和發送客戶端“收到啦,服務端”消息的動作事件方法 sendData:;添加關聯就可以瞭,比較簡單,下面來看一下具體實現的代碼:

ViewController.h

#import 

#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

ViewController.m

#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 來實現的,都是比較簡單的基本數據流操作。

微笑如有哪些不對的地方,歡迎指出!

發佈留言