NSStream

概念

流用于在不同介质间交换数据。三个相关的类为,定义基础接口和属性的NSStream,子类输入流NSInputStream以及子类输出流NSOutputStream

  • NSInputStream:从文件、Socket、Data中读取数据。
  • NSOutputStream:向文件、Socket、Data以及缓存中写入数据。
  • NSStream:公共接口属性。其中属性主要是用于处理网络安全和配置的,这些属性统称为SSL和SOCKS代理信息。

方法

  • 输入流:使用read:maxLength:方法从流中获取数据
  • 输出流:使用write:maxLength:方法将数据写入

代理

可以给流对象指定一个代理对象。如果没有指定,则流对象作为自己的代理。流对象调用唯一的代理方法stream:handleEvent:来处理流相关的事件。
代理传回NSStreamEvent枚举:

  • NSStreamEventNone:无事发生
  • NSStreamEventOpenCompleted:流成功开启
  • NSStreamEventHasBytesAvailable:流中有数据可读。代理向该stream发送read:maxLength:消息,从流中读取数据
  • NSStreamEventHasSpaceAvailable:可以向流中写入数据。代理向该stream发送write:maxlength:消息,向流中写入数据
  • NSStreamEventErrorOccurred:发生错误
  • NSStreamEventEndEncountered:流结束

使用

  1. 创建和初始化一个NSStream实例,并设置代理
  2. 将流对象放入一个run loop中并打开流
  3. 处理流对象发送到其代理的事件
  4. 结束时,关闭、从run loop移除并销毁流对象,
//连接方法中初始化,执行12步
-(void)connect:(NSString *)ipAdr port:(NSInteger)port status:(NSInteger)status
{
    DDLog(@"mogujie mtalk client :connect ipAdr:%@ port:%ld",ipAdr,(long)port);
    cDataLen = 0;
    
    _receiveBuffer = [NSMutableData data];
    _sendBuffers = [NSMutableArray array];
    _noDataSent = NO;
    
    _receiveLock = [[NSLock alloc] init];
    _sendLock = [[NSLock alloc] init];
    
    //创建和初始化一个`NSStream`实例
    NSInputStream  *tempInput  = nil;
    NSOutputStream *tempOutput = nil;
        
    [NSStream getStreamsToHostNamed:ipAdr port:port inputStream:&tempInput outputStream:&tempOutput];
    _inStream  = tempInput;
    _outStream = tempOutput;

    //设置代理
    [_inStream setDelegate:self];
    [_outStream setDelegate:self];

    //将流对象放入一个`run loop`中
    [_inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [_outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    
    //打开流
    [_inStream open];
    [_outStream open];
}

//代理方法 执行3步
#pragma mark - NSStream Delegate
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    switch(eventCode) {
        case NSStreamEventNone:
            DDLog(@"Event type: EventNone");
            break;
        case NSStreamEventOpenCompleted:
            //私有方法,内部执行发送连接成功通知,设置用户上线
            [self p_handleConntectOpenCompletedStream:aStream];
            break;
        case NSStreamEventHasSpaceAvailable:          
            //私有方法,内部执行线程锁,read:maxLength:读取数据,简单数据处理
            [self p_handleEventHasSpaceAvailableStream:aStream];
            break;
        case NSStreamEventErrorOccurred:
            //私有方法,内部执行出错时报错并断开连接,设置用户下线
            [self p_handleEventErrorOccurredStream:aStream];
            break;
        case NSStreamEventEndEncountered:
            //私有方法,内部执行数据长度清空
            [self p_handleEventEndEncounteredStream:aStream];
            break;
        case NSStreamEventHasBytesAvailable:
            //私有方法,内部执行线程锁,数据处理,并使用write:maxLength:写入数据
            [self p_handleEventHasBytesAvailableStream:aStream];
            break;
    }
}

//断开连接时关闭和销毁流
-(void)disconnect
{
    DDLog(@"MTalk Client:disconnect");
    
    cDataLen = 0;
    
    _receiveLock = nil;
    _sendLock = nil;
    if(_inStream)
    {
        [_inStream close];
        [_inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    _inStream = nil;
    
    if (_outStream) {
        [_outStream close];
        [_outStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    _outStream = nil;
    
    _receiveBuffer = nil;
    _sendBuffers = nil;
    _lastSendBuffer = nil;
    
    [MTTNotification postNotification:DDNotificationTcpLinkDisconnect userInfo:nil object:nil];
}

源码

DDTcpClientManager中额外提供writeToSocket公开方法。

#import "DDSuperAPI.h"
- (void)requestWithObject:(id)object Completion:(RequestCompletion)completion
{
    //seqNo
    theSeqNo ++;
    _seqNo = theSeqNo;
    //注册接口
    BOOL registerAPI = [[DDAPISchedule instance] registerApi:(id)self];
    
    if (!registerAPI)
    {
        return;
    }
    
    //注册请求超时
    if ([(id)self requestTimeOutTimeInterval] > 0)
    {
        [[DDAPISchedule instance] registerTimeoutApi:(id)self];
    }
    
    //保存完成块
    self.completion = completion;

    
    //数据打包
    Package package = [(id)self packageRequestObject];
    NSMutableData* requestData = package(object,_seqNo);
    
    //发送
    if (requestData)
    {
        //网络请求发送数据
        [[DDAPISchedule instance] sendData:requestData];
//        [[DDTcpClientManager instance] writeToSocket:requestData];
    }
}

#import "DDAPISchedule.h"
.
.
.
//放入自定义线程中执行
- (void)sendData:(NSMutableData*)data
{
    dispatch_async(self.apiScheduleQueue, ^{
        [[DDTcpClientManager instance] writeToSocket:data];
    });
}
.
.
.

#import "DDTcpClientManager.h"
//这里其实是底层使用socket和stream实现网络请求的一种方式
-(void)writeToSocket:(NSMutableData *)data{
    [_sendLock lock];
    
    @try {
        if (_noDataSent ==YES) {
            
            NSInteger len;
            len = [_outStream write:[data mutableBytes] maxLength:[data length]];
            _noDataSent = NO;
            DDLog(@"WRITE - Written directly to outStream len:%li", (long)len);
            if (len < [data length]) {
                DDLog(@"WRITE - Creating a new buffer for remaining data len:%u", [data length] - len);
                _lastSendBuffer = [DDSendBuffer dataWithNSData:[data subdataWithRange:NSMakeRange([data length]-len, [data length])]];
                [_sendBuffers addObject:_lastSendBuffer];
                
            }
            return;
        }
        
        if (_lastSendBuffer) {
            NSInteger lastSendBufferLength;
            NSInteger newDataLength;
            lastSendBufferLength = [_lastSendBuffer length];
            newDataLength = [data length];
            if (lastSendBufferLength<1024) {
                DDLog(@"WRITE - Have a buffer with enough space, appending data to it");
                [_lastSendBuffer appendData:data];
                return;
            }
        }
        DDLog(@"WRITE - Creating a new buffer");
        _lastSendBuffer = [DDSendBuffer dataWithNSData:data];
        [_sendBuffers addObject:_lastSendBuffer];
        
    }
    @catch (NSException *exception) {
        DDLog(@" ***** NSException:%@ in writeToSocket of MGJMTalkClient *****",exception);
    }
    @finally {
        [_sendLock unlock];
    }
}

你可能感兴趣的:(NSStream)