当AsyncSocket无法二次封装---苹果原生Stream流的救赎


干货:自定义Socket类

Socket.h

@protocol SocketDelegate //代理方法传值出去

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

@end

@interface Socket : NSObject

- (void)socketConHost:(NSData *)message; //socket连接
- (void)sendMessage:(NSData *)message;//发送消息

@property(nonatomic,assign) iddataDelegate;//代理
@end

Socket.m

@interface Socket(){//遵守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左右,效率可放心。

有任何问题欢迎留言,大家互相探讨。

你可能感兴趣的:(当AsyncSocket无法二次封装---苹果原生Stream流的救赎)