美文网首页iOS Developer程序员iOS分享的demo
当AsyncSocket无法二次封装---苹果原生Stream流

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

作者: 宋唐不送糖 | 来源:发表于2016-08-25 17:22 被阅读163次

    干货:自定义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左右,效率可放心。

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

    相关文章

      网友评论

        本文标题:当AsyncSocket无法二次封装---苹果原生Stream流

        本文链接:https://www.haomeiwen.com/subject/dkdasttx.html