當AsyncSocket無法二次封裝---蘋果原生Stream流的救贖

weixin_34075551發表於2016-08-25

乾貨:自定義Socket類

Socket.h

@protocol SocketDelegate <NSObject>//代理方法傳值出去

- (void)didReadData:(NSData *)data;

@end

@interface Socket : NSObject

- (void)socketConHost:(NSData *)message; //socket連線
- (void)sendMessage:(NSData *)message;//傳送訊息

@property(nonatomic,assign) id<SocketDelegate>dataDelegate;//代理
@end

Socket.m

@interface Socket()<NSStreamDelegate>{//遵守stream協議
// 輸入流,用來讀取伺服器返回的位元組
NSInputStream *_inputStream;
// 輸出流,用於給伺服器傳送位元組
NSOutputStream *_outputStream;
}
@end

@implementation Socket

- (void)socketConHost:(NSData *)message{   
  // 建立CF下的讀入流
  CFReadStreamRef readStream;
  // 建立CF下的寫出流
  CFWriteStreamRef writeStream;   
  NSString *host = @"XXX.XXX.XXX.XXX";
  int port = XXXX;    
  // 建立流
  CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(host), port, &readStream, &writeStream);   
  // 將CFXXX流和NSXXX流建立對應關係
  _inputStream = (__bridge NSInputStream *)(readStream);
  _outputStream = (__bridge NSOutputStream *)(writeStream);   
  // 設定通訊過程中的代理
  _inputStream.delegate = self;
  _outputStream.delegate = self;   
  // 將流物件新增到主執行迴圈(如果不加到主迴圈,Socket流是不會工作的)
  [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
  [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
  // 開啟流
  [_inputStream open];
  [_outputStream open]; 
}

#pragma mark stream的代理方法
-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{
NSLog(@"%lu",eventCode);
switch (eventCode) {
    case NSStreamEventOpenCompleted:
        NSLog(@"連線完成");
        break;
    case NSStreamEventHasBytesAvailable:
        NSLog(@"有可讀位元組");
        [self readData];
        break;
    case NSStreamEventHasSpaceAvailable:
        NSLog(@"可以寫入資料");
        break;
    case NSStreamEventErrorOccurred:
        NSLog(@"發生錯誤");
        break;
    case NSStreamEventEndEncountered:
        NSLog(@"流結束");
        // 做善後工作
        // 關閉流的同時,將流從主執行迴圈中刪除
        [aStream close];
        [aStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        [aStream setDelegate:nil];
        break;
    default:
        break;
  }  
}

#pragma mark 傳入要傳送的資料
- (void)sendMessage:(NSData *)message
{
  [_outputStream write:message.bytes maxLength:message.length];
}

#pragma mark 讀了伺服器返回的資料
-(void)readData{    
  //建立一個緩衝區 可以放1024個位元組
  uint8_t buf[1024]; 
  // 返回實際裝的位元組數
  NSInteger len = [_inputStream read:buf maxLength:sizeof(buf)];  
  // 把位元組陣列轉化成字串
  NSData *data = [NSData dataWithBytes:buf length:len]; 
  // 從伺服器接收到的資料
  @try {
      if ([_dataDelegate respondsToSelector:@selector(didReadData:)]) {
        [_dataDelegate didReadData:data];//代理方法把資料傳出
      }
  } 
  @catch (NSException *exception) {    
  } 
  @finally {    
  } 
}

#pragma mark 釋放
- (void)dealloc {
  //釋放輸入輸出流   
  if (_outputStream) {
    [_outputStream close];
    [_outputStream removeFromRunLoop:[NSRunLoop mainRunLoop]forMode:NSDefaultRunLoopMode];
    _outputStream = nil;
  }
  if (_inputStream) {
    [_inputStream close];
    [_inputStream removeFromRunLoop:[NSRunLoop mainRunLoop]forMode:NSDefaultRunLoopMode];       
    _inputStream = nil;
  }
  //注意!!!呼叫父類方法要放在最後,否則程式會報錯,具體原因似乎是蘋果的機制:先幹掉大頭,再幹掉小的(所以是mrc)
  [super dealloc];
}

至此封裝結束,外部呼叫實現代理方法即可獲取到值。測試拿到資料後返回外部接收在1ms左右,效率可放心。

有任何問題歡迎留言,大家互相探討。

相關文章