NSOutputStream 没有调用委托的 NSStreamEventHasSpaceAvailable

Posted

技术标签:

【中文标题】NSOutputStream 没有调用委托的 NSStreamEventHasSpaceAvailable【英文标题】:NSOutputStream not calling delegate's NSStreamEventHasSpaceAvailable 【发布时间】:2013-09-29 18:24:50 【问题描述】:

我已经通过使用输入和输出流实现了套接字。外部架构负责一次发送一个写入请求。

但是,如果任何请求没有返回 no HasBytesAvailable,我需要从队列中删除该请求并通知请求超时。

对于所有其他请求,我能够正确发送/接收数据,但如果任何一个请求超时,那么之后HasSpaceAvailable 将永远不会被调用。

我的代码如下:

@implementation CCCommandSocket

@synthesize 连接超时定时器; @synthesize requestTimeoutTimer;

/* * 在里面 * * @参数 * ipAddress :摄像头socket的ip地址 * portNumber :摄像头socket的端口地址 * * @返回 * Socket类型的对象,它将向ipAddress,portNumber发送连接请求 * */ - (id)初始化 self = [超级初始化]; 如果(自己) ip = @"192.168.42.1"; 端口 = 7878;

    [self performSelectorOnMainThread:@selector(connectToCamera) withObject:nil waitUntilDone:YES];

    bytesReceivedCondition = [[NSCondition alloc] init];
    requestCompletedCondition = [[NSCondition alloc] init];
    requestReadyToProcess = [[NSCondition alloc] init];
    isBytesReceived = false;
    isRequestCompleted = false;
    isRequestReadyToProcess = false;
    responseString = [[NSString alloc] init];
    openBracesCount = 0;

    mutex = [[NSLock alloc] init];

return self;

pragma mark-

pragma 建立套接字通信。

/* * 连接相机 * */ -(无效)connectToCamera NSString *urlStr = ip;

if (![urlStr isEqualToString:@""])


    NSURL *website = [NSURL URLWithString:urlStr];

    if (!website)
    
        NSString* messageString = [NSString stringWithFormat:@"%@ is not a valid URL",website];
        CCLog(LOG_ERROR, messageString);
        return;
    

    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(urlStr), port, &readStream, &writeStream);

    //cast the CFStreams to NSStreams
    inputStream = (__bridge_transfer NSInputStream *)readStream;
    outputStream = (__bridge_transfer NSOutputStream *)writeStream;

    //set the delegate
    [inputStream setDelegate:self];
    [outputStream setDelegate:self];

    //schedule the stream on a run loop
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

    //open the stream
    [inputStream open];
    [outputStream open];

    if(readStream==NULL)
    
        CCLog(LOG_INFO, @"readstream NULL");
    

    if(writeStream == NULL)
    
        CCLog(LOG_INFO, @"writeStream NULL");
    

    [self startConnectionTimeoutTimer];

pragma mark -

pragma getter 方法

/* * 获取IP * * @返回 * 套接字连接的IP地址 */ -(NSString *) 获取IP 返回IP;

/* * 获取端口 * * @返回 * 套接字连接的端口号 */ -(int) 获取端口 返回端口;

pragma mark-

pragma 处理套接字回调。

(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode

NSMutableArray *array = [[NSMutableArray alloc] init]; [数组添加对象:流]; [数组addObject:[NSNumber numberWithInt:eventCode]];

[self performSelectorInBackground:@selector(myStream:) withObject:array];

(void)myStream:(NSMutableArray*) 数组 NSNumber *number = [数组 objectAtIndex:1]; int eventCode = [number intValue];

开关(事件代码) case NSStreamEventErrorOccurred: CCLog(LOG_ERROR, @"In Command Socket NSStreamEventErrorOccurred"); //[自我断开]; //[[ErrorDetails getInstance] reportError:NSStreamEventErrorOccurred]; 休息;

    //Read from stream
case NSStreamEventHasBytesAvailable:


    CCLog(LOG_INFO, @"In Command Socket NSStreamEventHasBytesAvailable");
    [self handleCommandPortDataReceived];
    break;


    //Write to stream
case NSStreamEventHasSpaceAvailable:

    @synchronized(self)
    
        [requestReadyToProcess lock];
        while (isRequestReadyToProcess == false)
        
            [requestReadyToProcess wait];
        
        [requestReadyToProcess unlock];

        CCLog(LOG_INFO,@"In Command Socket NSStreamEventHasSpaceAvailable");

        @try
        
            @synchronized(requestString)
            
                if(requestString != nil)
                
                    if(outputStream != nil)
                    
                        int dataSent;

                        uint8_t* data = (uint8_t *)[requestString cStringUsingEncoding:NSUTF8StringEncoding];
                        responseString = @"";

                        //[requestReadyToProcess lock];
                        isRequestReadyToProcess = false;
                        //[requestReadyToProcess signal];
                        dataSent = [outputStream write:data maxLength:strlen((char*)data)];

                        if(dataSent != -1)
                        
                            NSString* message = [NSString stringWithFormat:@"Bytes written %d for request\n %@",dataSent, requestString];
                            CCLog(LOG_REQUEST, message);
                            requestString = nil;
                            isBytesReceived = false;
                            [bytesReceivedCondition lock];

                            while (isBytesReceived ==false)
                            
                                [bytesReceivedCondition wait];
                            
                            [requestCompletedCondition lock];
                            isRequestCompleted = true;
                            [requestCompletedCondition signal];
                            [requestCompletedCondition unlock];
                            [bytesReceivedCondition unlock];
                        
                        else
                        
                            CCLog(LOG_INFO, @"Command Socket : Request not sent (dataSent == -1)");
                            responseString = @" \"rval\": -104";
                            CCLog(LOG_RESPONSE, responseString);

                            [self removeRequestFromQueue];
                        
                    
                    else
                    
                        CCLog(LOG_INFO, @"in else :(outputStream != nil)");
                    
                
            
        
        @catch (NSException *e)
        
            CCLog(LOG_WARNING, e.description);
        
    
    break;

case NSStreamEventNone:

    CCLog(LOG_INFO, @"In Command Socket NSStreamEventNone");
    break;

case NSStreamEventOpenCompleted:

    CCLog(LOG_INFO, @"In Command Socket NSStreamEventOpenCompleted");
    [self stopConnectionTimeoutTimer];
    break;

case NSStreamEventEndEncountered:

    CCLog(LOG_INFO, @"Command Socket NSStreamEventEndEncountered");

    [self disconnectWithNotification:YES];
    break;

/* * 执行 * * @参数 * 请求:通过套接字发送到相机的命令 * * @返回 *响应:从相机收到的响应 * */ -(NSString *)执行请求:(NSString *)请求 CCLog(LOG_INFO, @"命令套接字执行请求");

[self performSelectorOnMainThread:@selector(startRequestTimeoutTimer) withObject:nil waitUntilDone:NO];

isRequestCompleted = false;
requestString = request;
responseString = @"";

[requestReadyToProcess lock];
isRequestReadyToProcess = true;
[requestReadyToProcess signal];
[requestReadyToProcess unlock];

[requestCompletedCondition lock];
while (isRequestCompleted ==false)

    [requestCompletedCondition wait];


CCLog(LOG_INFO, @"Command Socket Execute request : request completed");
[requestCompletedCondition unlock];
CCLog(LOG_RESPONSE, responseString);
return responseString;

pragma mark-

pragma Handle 连接超时

// 启动连接时调用这个 - (void)startConnectionTimeoutTimer [self stopConnectionTimeoutTimer]; // 或者确保在调用此方法之前停止任何现有的计时器

NSTimeInterval interval = 10.0; // Measured in seconds, is a double

self.connectionTimeoutTimer = [NSTimer scheduledTimerWithTimeInterval:interval
                                                               target:self
                                                             selector:@selector(handleConnectionTimeout)
                                                             userInfo:nil
                                                              repeats:NO];

(void)handleConnectionTimeout responseString = @" \"rval\": -103"; CCLog(LOG_RESPONSE, responseString);

[self removeRequestFromQueue];

[self disconnectWithNotification:YES]; [self stopConnectionTimeoutTimer];

// 启动连接时调用这个 - (void)startRequestTimeoutTimer [self stopRequestTimeoutTimer]; // 或者确保在调用此方法之前停止任何现有的计时器

NSTimeInterval interval = 20.0; // Measured in seconds, is a double

self.requestTimeoutTimer = [NSTimer scheduledTimerWithTimeInterval:interval
                                                               target:self
                                                             selector:@selector(handleRequestTimeout)
                                                             userInfo:nil
                                                              repeats:NO];

(void)handleRequestTimeout responseString = @" \"rval\": -103"; CCLog(LOG_RESPONSE, responseString);

[自连接相机]; [self stopRequestTimeoutTimer]; [自我 removeRequestFromQueue];

// 连接成功后调用 - (void)stopRequestTimeoutTimer 如果(请求超时定时器) [requestTimeoutTimer 无效]; requestTimeoutTimer = nil;

-(void) disconnectWithNotification:(BOOL)showNotification CCLog(LOG_INFO, @"套接字断开"); [输入流关闭]; [inputStream setDelegate:nil]; [inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; inputStream = nil;

[outputStream close];
[outputStream setDelegate:nil];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
                        forMode:NSDefaultRunLoopMode];
outputStream = nil;
[[CCCore getInstance] disconnectWithNotification:showNotification];

// 连接成功后调用 - (void)stopConnectionTimeoutTimer 如果(连接超时定时器) [connectionTimeoutTimer 无效]; 连接超时定时器 = 无; 如果(请求超时定时器) [requestTimeoutTimer 无效]; requestTimeoutTimer = nil;

-(void) handleCommandPortDataReceived [互斥锁]; [self stopRequestTimeoutTimer]; @尝试 长尺寸 = 1024; uint8_t buf[大小]; 无符号 int len = 0;

    do
    
        // read input stream into buffer
        strcpy((char *)buf, "\0");
        len = [inputStream read:buf maxLength:size];

        //NSLog(@"Size = %ld Len = %d, Buf = %s",size, len, (char *)buf);

        // Following code checks if we have received complete response by matching "" and ""
        // from input stream. We continue to form response string unless braces are matched.
        if (len > 0)
        
            // Create nsdata from buffer
            NSMutableData *_data = [[NSMutableData alloc] init];
            [_data appendBytes:(const void *)buf length:len];

            // create temporary string form nsdata
            NSString* currentString = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding];

            // check the occurances of  and  in current string
            int currentOpeningBraceCount = [[currentString componentsSeparatedByString:@""] count] - 1;
            int currentClosingBraceCount = [[currentString componentsSeparatedByString:@""] count] - 1;

            openBracesCount = (openBracesCount + currentOpeningBraceCount) - currentClosingBraceCount;
            responseString = [responseString stringByAppendingString:currentString];

            //                        NSLog(@"Total:%d currentOpen:%d currentClose:%d\n\n",openBracesCount, currentOpeningBraceCount, currentClosingBraceCount);
            //                        NSLog(@"Current String : %@\n\n",currentString);
            //                        NSLog(@"Final String : %@",finalString);
            //                        NSLog(@"+++++++++++++++++++++++++++++");
        
        else
            break;

     while (openBracesCount != 0);


    NSRange range = [responseString rangeOfString:@"get_file_complete"];
    if(range.location == NSNotFound)
    
        //remove it from queue
        [bytesReceivedCondition lock];
        isBytesReceived = true;
        [bytesReceivedCondition signal];
        [bytesReceivedCondition unlock];
    
    //responseString = @"";


@catch (NSException* e)

    [self connectToCamera];

[mutex unlock];

-(void) removeRequestFromQueue //从队列中移除 requestString = nil;

[requestReadyToProcess lock];
isRequestReadyToProcess = false;
[requestReadyToProcess unlock];

[requestCompletedCondition lock];
isRequestCompleted = true;
[requestCompletedCondition signal];
[requestCompletedCondition unlock];

@结束

【问题讨论】:

【参考方案1】:

您在哪个操作系统版本上试用?我遇到了类似的问题,在 10.7 及更高版本中一切都很好,但在 10.6 及更低版本中,我遇到了同样的问题,我正在做一些调试,但到目前为止还没有提出好的解决方案。

【讨论】:

以上是关于NSOutputStream 没有调用委托的 NSStreamEventHasSpaceAvailable的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionView 未显示 & 未调用委托/数据源方法

如果实现类被标记为私有,则不调用委托方法?

尝试使用尚未从自身检索的数据后调用委托方法 didUpdateLocations - Xcode Swift

iOS:用于大文件下载的 NSFileHandle 与 NSOutputStream

iOS开发系列-NSOutputStream

无法调配 NSOutputStream 的 write:MaxLength: