iOS: fundamentos de networkinges de socket que utilizan CFStreamCreatePairWithSocketToHost

Caso de uso

Estoy usando sockets para enviar y recibir datos usando CFStreamCreatePairWithSocketToHost() y estoy tratando de entender cómo se hace esto al enviar múltiples sets de datos (es decir, no solo una request ).

Problema

Actualmente puedo enviar datos y recibir una respuesta (es decir, 1 ida y vuelta). Sin embargo, después de enviar todos los datos en outputStream la transmisión se cierra (es decir, recibe NSStreamEventEndEncountenetworking ).

Pregunta

Entonces, la pregunta es: ¿qué sucede cuando quiero enviar varias requestes de datos?

  • ¿Configuro un nuevo socket cada vez que tengo un nuevo object de datos para enviar?
  • ¿Debo reiniciar outputStream y enviar más datos?

Código

La mayor parte de este código proviene de la documentation de Cocoa Streams :

 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _data = [[NSMutableData alloc] init]; [self initNetworkCommunication]; [self sendString:@"Hello World!"]; } - (void)initNetworkCommunication { CFReadStreamRef readStream; CFWriteStreamRef writeStream; CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"123.456.0.0", 1234, &readStream, &writeStream); inputStream = (NSInputStream *)readStream; // ivar [inputStream setDelegate:self]; [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [inputStream open]; outputStream = (NSOutputStream *)writeStream; // ivar [outputStream setDelegate:self]; [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outputStream open]; } - (void)sendString:(NSString *)string { NSData *data = [[NSData alloc] initWithData:[string dataUsingEncoding:NSASCIIStringEncoding]]; [_data appendData:data]; [data release]; } - (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent { NSLog(@"stream event %u", streamEvent); switch (streamEvent) { case NSStreamEventOpenCompleted: NSLog(@"Stream opened"); break; case NSStreamEventHasSpaceAvailable: { uint8_t *readBytes = (uint8_t *)[_data mutableBytes]; readBytes += byteIndex; // ivar int data_len = [_data length]; unsigned int len = ((data_len - byteIndex >= 1024) ? 1024 : (data_len - byteIndex)); uint8_t buf[len]; (void)memcpy(buf, readBytes, len); len = [(NSOutputStream *)theStream write:(const uint8_t *)buf maxLength:len]; NSLog(@"Sending buffer of len: %d", len); byteIndex += len; break; } case NSStreamEventHasBytesAvailable: if (theStream == inputStream) { uint8_t buffer[1024]; int len; while ([inputStream hasBytesAvailable]) { len = [inputStream read:buffer maxLength:sizeof(buffer)]; if (len > 0) { NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding]; if (nil != output) { NSLog(@"server said: %@", output); } } } [self sendString:@"Another Test"]; } break; case NSStreamEventErrorOccurnetworking: NSLog(@"Can not connect to the host!"); break; case NSStreamEventEndEncountenetworking: NSLog(@"Closing stream..."); [theStream close]; [theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [theStream release]; theStream = nil; break; default: NSLog(@"Unknown event"); } } 

Respuesta:

 2012-08-15 08:16:30.896 Sockets[34836:f803] Opened input stream. 2012-08-15 08:16:30.898 Sockets[34836:f803] Opened output stream. 2012-08-15 08:16:30.899 Sockets[34836:f803] Sending buffer of len: 12 2012-08-15 08:16:30.900 Sockets[34836:f803] Sending buffer of len: 0 2012-08-15 08:16:30.901 Sockets[34836:f803] Closing output stream. 2012-08-15 08:16:30.939 Sockets[34836:f803] server said: Hello World! 

Tenga en count que la secuencia outputStream cierra después de que envíe los datos. Intento reiniciar outputStream antes [self sendString:@"Another Test"]; . También probé la respuesta de Idz .

Según la documentation, creo que el búfer de envío de Len 0 es mi problema.

Si el delegado recibe un evento NSStreamEventHasSpaceAvailable y no escribe nada en la transmisión, no recibe más events disponibles en el espacio desde el ciclo de ejecución hasta que el object NSOutputStream recibe más bytes. Cuando esto sucede, el ciclo de ejecución se reinicia para los events disponibles en el espacio.

Sin embargo, la documentation no dice nada acerca de cerrar la transmisión cuando se alcanza el final de la transmisión. Entonces estoy confundido …

Un socket es un flujo bidireccional que conecta dos progtwigs, posiblemente en dos computadoras diferentes. Simplemente transfiere los datos que escribe en un extremo para que se lean en el otro extremo. No hace cumplir ninguna estructura en los datos y no sabe nada sobre requestes o respuestas.

La API CFStreamCreatePairWithSocketToHost divide una única connection en dos flujos independientes, uno de los que puede leer y otro en el que puede escribir. Este es un buen toque, la API de socket subyacente usa solo un descriptor de file tanto para leer como para escribir, lo que puede resultar bastante confuso.

La connection permanece abierta hasta que un lado cierre el zócalo. También existe la posibilidad de apagar el zócalo solo en una dirección. También es posible cerrar la connection solo en una dirección. Si el control remoto se cierra, se lee la transmisión, su secuencia de escritura se cerrará y viceversa.

¿Configuro un nuevo socket cada vez que tengo un nuevo object de datos para enviar?

Debes evitar hacer eso. Lleva algún time establecer una nueva connection, y toma incluso más time antes de que su connection llegue a toda velocidad. Por lo tanto, debe reutilizar la misma connection tanto como sea posible.

¿Debo reiniciar outputStream y enviar más datos?

No, esto no es necesario, simplemente envíe más datos.

Según la documentation, creo que el búfer de envío de Len 0 es mi problema.

Escribir nada (es decir, un búfer de longitud 0) no debería ser un problema. La documentation no especifica qué pasará sin embargo. Entonces escribí el progtwig de testing hoy para ver qué pasará, sin esperar nada. Como resulta, escribir un búfer de longitud 0 cierra el flujo de salida. Entonces este fue realmente tu problema. Presentaré un error en el Apple Bug Reporter sobre ese problema de documentation, y tú también.

La parte de la documentation que citó trata sobre algo diferente. Si no escribe después de recibir una notificación de espacio disponible, no recibirá otra hasta que haya escrito algo. Eso es útil, porque el sistema no desperdicia ciclos de CPU para decirle a su código una y otra vez que podría escribir algo si no tiene nada que escribir.