2011-11-03 11 views
7

Sto cercando di ottenere un semplice esempio di lavoro con GCDAsyncSocket e sto scoprendo che mi mancano alcune parti di comprensione e spero che la gente possa aiutare a spiegare questo.Come posso ottenere didReadData all'interno di GCDAsyncSocket eseguito all'interno del RunLoop corrente?

Ho installato la roba GCDAsyncSocket di seguito:

dispatch_queue_t mainQueue = dispatch_get_main_queue(); 
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:mainQueue]; 

NSString *host = @"192.168.169.132"; 
uint16_t port = 2112; 

DDLogInfo(@"Connecting to \"%@\" on port %hu...", host, port); 
self.viewController.label.text = @"Connecting..."; 

NSError *error = nil; 
if (![asyncSocket connectToHost:host onPort:port withTimeout:5.0 error:&error]) 
{ 
    DDLogError(@"Error connecting: %@", error); 
    self.viewController.label.text = @"Oops"; 
} 
else 
{ 
    DDLogVerbose(@"Connecting..."); 
} 


- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port 
{ 
    DDLogInfo(@"socket:%p didConnectToHost:%@ port:%hu", sock, host, port); 
    self.viewController.label.text = @"Connected"; 

    // We're just going to send a test string to the server. 

    NSString *myStr = @"testing...123...\r\n"; 
    NSData *myData = [myStr dataUsingEncoding:NSUTF8StringEncoding]; 

    [asyncSocket writeData:myData withTimeout:5.0 tag:0]; 
} 

E riesco a vedere la mia presa di application server di test riceve la stringa

"test ... 123 ... \ r \ n "

Ma quando poi il mio server di prova presa inviare una stringa indietro, ho ingenuamente previsto il didReadData delegato per eseguire

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag 

Eppure la realtà nuda e cruda mi ha costretto a sapere che fino a quando io chiamo

[asyncSocket readDataWithTimeout:5.0 tag:0]; 

... il didReadData delegato non sarà possibile ottenere chiamato.

OK, va bene. Capisco.

lettura su the documentation ancora un po 'si afferma chiaramente che

AsyncSocket è una libreria di socket TCP basato runloop.

Così ora sto guardando questo runloop cosa, che a mio avviso è come il Message loop in Microsoft Windows. Essendo che iOS è un'architettura basata su eventi/msg (proprio come Win32), quindi il thread principale di default che ho attualmente ha ovviamente il proprio ciclo msg per gestire gli eventi.

La mia confusione ora sta avendo iOS RunLoop sembra una entità separata con cui lavorare per far funzionare GCDAsyncSocket correttamente.

Quando viene indicato che il suo insieme predefinito di modalità ciclo di esecuzione è NSDefaultRunLoopMode, che si trova nella thread principale.

Confuso ancora?

Quindi sotto Win32 il mio codice di gestione degli eventi comm sarebbe simile a questa:

while(sCOMport.hCOMport != INVALID_HANDLE_VALUE) // ...while the COM port is open... 
{ 
    // Wait for an event to occur on the port. 
    WaitCommEvent(sCOMport.hCOMport, &dwCommStatus, NULL); 

Sarebbe ovviamente nel proprio thread (non hanno ottenuto ancora arrivati ​​utilizzando GCDAsyncSocket), ma sarebbe un proprio "RunLoop" in un certo senso.

come faccio a fare lo stesso utilizzando GCDAsyncSocket in modo che io non sono bloccato in un certo ciclo di polling riempire la coda con [asyncSocket readDataWithTimeout] chiamate?

Penso che abbiamo bisogno di esempi migliori nell'utilizzo di questa libreria.

risposta

18

Ok, ho funzionato in qualche modo.

Fammi sapere se questo va contro alcune "migliori pratiche".

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port 
{ 
    // setup as normal... 

    // then ... 

    // Instigate the first read 
    [asyncSocket readDataWithTimeout:-1 tag:0]; 

. 
. 
} 

Poi ... quando il dato è disponibile in ...

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag 
{ 
    // Do whatever you need to do with the data ... 
. 
. 
. 
    // and at the end ... 
. 
. 
    // Always keep a 'read' in the queue. 
    [asyncSocket readDataWithTimeout:-1 tag:0]; 
} 

Questo vi darà l'operazione runloop senza utilizzare i timer o altri costrutti. E può essere racchiuso all'interno del proprio thread. (Che è ancora TBD però)

+1

In realtà ho fatto la stessa cosa. Basta "ri-accodare" l'ascoltatore ancora e ancora. Anch'io sono piuttosto curioso di sapere se questa è la strada da percorrere o meno. Come te, mi sarei aspettato che ReadData venisse chiamato senza bisogno di fare nulla. Non è tutta la ragione per le prese ... – renevdkooi

+0

Sono così confuso al momento che devo usare il cluster di suck per ricevere i dati appena sono arrivati. : S – Rihards

1

So che questa è una vecchia questione e già avere una risposta accettata, ma qui è la mia soluzione che ho già utilizzare in una delle mie applicazioni:

dopo la connessione a un host eseguire il seguente coda di spedizione:

dispatch_queue_t alwaysReadQueue = dispatch_queue_create("com.cocoaasyncsocket.alwaysReadQueue", NULL); 

dispatch_async(alwaysReadQueue, ^{ 
    while(![socket isDisconnected]) { 
     [NSThread sleepForTimeInterval:5]; 
     [socket readDataWithTimeout:-1 tag:0]; 
    } 
}); 

Avresti potuto utilizzare la stessa coda per inviare un battito cardiaco richieste solo per mantenere attiva la connessione.

+0

Non sta andando a mantenere un forte fermo sul socket e prevenire deallocazione? Raccomanderei di usare 'while (! [WeakSocket isDisconnected])' e poi dopo il sonno usando '__strong GCDAsyncSocket * strongSocket = weakSocket;' quindi controllando se è nullo e se si rompe se lo è. Anche se si usa un riferimento debole al socket, il condizionale '[socket is Disconnected]' darà come risultato NO che quando negato risulterà in SÌ, il che significa che la spedizione non uscirà mai. Potrei sbagliarmi però. –