TCP-соединения с NSStream / CFStream - PullRequest
1 голос
/ 22 декабря 2011

Я отчаянно пытаюсь выяснить, как обнаруживать ошибки при открытии потоков 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. Если я telnet на тот же адрес, я получаю отказано в соединении - ничего не прослушивает порт 80. Если я пытаюсь подключиться к некоторому бессмысленному адресу, например yaddayadda, я получаю nil потоков.

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

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

Ответы [ 3 ]

2 голосов
/ 05 апреля 2013

Я столкнулся с той же проблемой, и вот как я это исправил.В моем случае я использовал 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;
1 голос
/ 22 декабря 2011

прочитайте руководство по потоку на apple LINK

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

 - (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");
        }
        }
}
0 голосов
/ 22 декабря 2011

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

...