TCP-соединения с NSStream/CFStream

Я отчаянно пытаюсь понять, как обнаруживать ошибки при открытии потоков TCP с использованием NSStream +getStreamsToHost/CFStreamCreatePairWithSocket(). Если я сделаю это:

NSInputStream* input = nil;
NSOutputStream* output = nil;
[NSStream getStreamstoHost:[NSHost hostWithName:@"localhost"] port:80 inputStream:&input outputStream:&output];

NSError* error1 = [input streamError];
assert(error1 == nil);
NSStreamStatus status1 = [input streamStatus];

[input open];
NSError* error2 = [input streamError];
assert(error2 == nil);
NSStreamStatus status2 = [input streamStatus];

status1 равно NSStreamStatusNotOpen, что и ожидается. error1 это nil. error2 это тоже nil, а status2 это NSStreamStatusOpening. Если я подключаюсь к тому же адресу, я получаю сообщение об отказе в соединении - на порту 80 ничего не прослушивается. Если я пытаюсь подключиться к какому-то бессмысленному адресу, например yaddayadda, я получаю nil потоков.

Как правильно обрабатывать ошибки? Кажется, ни один пример нигде не обрабатывает условия ошибки, и документы ничего не говорят об этом, за исключением того, что потоки могут быть нулевыми. Я в тупике. Не говорите мне, что мне нужно запустить соединение через цикл выполнения, просто чтобы получить правильную обработку ошибок...?

Я знаю, что всегда есть возможность использовать старые добрые сокеты BSD, но документация предостерегает от этого, так как некоторые сетевые функции высокого уровня могут выйти из строя (автоматическое подключение через VPN и тому подобное).


person Jörgen Sigvardsson    schedule 21.12.2011    source источник


Ответы (3)


Я столкнулся с той же проблемой, и вот как я ее исправил. В моем случае я использовал SSL, но вы можете пропустить эту часть кода.

NSString *host = @"10.38.129.234";

CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;

CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)host, 403, &readStream, &writeStream);

[readStream setProperty:NSStreamSocketSecurityLevelSSLv3 forKey:NSStreamSocketSecurityLevelKey];
[readStream setProperty:(id)kCFBooleanFalse forKey:(NSString *)kCFStreamPropertyShouldCloseNativeSocket];
[writeStream setProperty:NSStreamSocketSecurityLevelSSLv3 forKey:NSStreamSocketSecurityLevelKey];
[writeStream setProperty:(id)kCFBooleanFalse forKey:(NSString *)kCFStreamPropertyShouldCloseNativeSocket];

//Setup SSL properties
NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
                          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
                          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
                          [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
                          @"10.38.129.234",kCFStreamSSLPeerName,
                          nil];
settings = [settings autorelease];

CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);

//Open the OutputStream
CFWriteStreamOpen(writeStream);
UInt8 buf[] = "your message ";
[self writeToStream:writeStream :buf];

//Read the Stream
CFReadStreamOpen(readStream);
NSString *response = [self readFromStream:readStream];
if(response != nil)
{
    NSLog(@"%@",response);
}

UInt8 pull[] = "another message\n";
[self writeToStream:writeStream :pull];

response = [self readFromStream:readStream];
if(response != nil)
{
    NSLog(@"%@",response);
}

//Close the readstream
CFReadStreamClose(readStream);
CFRelease(readStream);
readStream = NULL;

//Close the writestream
CFWriteStreamClose(writeStream);
CFRelease(writeStream);
writeStream = NULL;
person thelastworm    schedule 05.04.2013

ознакомьтесь с руководством по потоку на сайте Apple ССЫЛКА

вы увидите, что у вас должен быть метод, который переключает потоковые события, например так

 - (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {

    switch (streamEvent) 
    {
                case NSStreamEventOpenCompleted:
        {
            DDLogVerbose(@"Stream opened");
                        break;
        }

        case NSStreamEventHasBytesAvailable:
        {
            if(!rawData) {
                rawData = [[NSMutableData data] retain];
            }
            uint8_t buf[1024];
            unsigned int len = 0;
            len = [(NSInputStream *)theStream read:buf maxLength:1024];
            if(len) {
                [rawData initWithBytes:buf length:len];                
            } else {
                DDLogVerbose(@"no buffer!");
            }
            const uint8_t *bytes = [rawData bytes];
            NSMutableArray *mutableBuffer = [[NSMutableArray alloc] initWithCapacity:len];  

            for (int i =0; i < [rawData length]; i++) {
                [mutableBuffer addObject:[NSString stringWithFormat:@"%02X", bytes[i]]];
            }
            [self gateKeeper:mutableBuffer];
            [mutableBuffer release];
            break;
        }       
                case NSStreamEventErrorOccurred:
        {
            if ([theStream isKindOfClass:[NSInputStream class]]) {
                NSString* address = [self getAddress];
                NSString* myIPAdress = [NSString stringWithFormat:@"IP Address: %@", address];

                //[cClient updateRequest];
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Cant Connect" message:[NSString stringWithFormat:@"Cant connect to server: %@, Make sure you are connected to the proper wireless network.  Your Ip Address is %@",CCV.ipAddress,myIPAdress] delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:@"Reconnect", nil];

                [alert show];
                [alert release];                        
            }
            break;   
        }

                case NSStreamEventEndEncountered:
        {
            [theStream close];
            [theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [theStream release];
            break;
        }
        case NSStreamEventHasSpaceAvailable:
        {
            //DDLogVerbose(@"has space available");
            break;
        }

        case NSStreamEventNone:
        {
            DDLogVerbose(@"none");
            break;
        }

                default:
        {
                        DDLogVerbose(@"Unknown event");
        }
        }
}
person owen gerig    schedule 21.12.2011

Что вам нужно сделать, так это либо опросить состояние, используя метод streamStatus для входного потока, либо запланировать потоки для событий в цикле выполнения. Чтобы лучше предоставлять информацию об ошибках в ошибочных именах хостов, лучше разрешить имя заранее, используя либо NSHost, либо CFHost.

person Jörgen Sigvardsson    schedule 21.12.2011