por pasar otro fotos datos conexion como buscar apple objective-c tcp nsstream

objective c - pasar - Análisis de datos después del envío entre dispositivos iOS a través de NSStream



itunes (2)

Tengo una aplicación configurada para enviar datos entre dos dispositivos iOS usando NSStream s a través de una conexión TCP.

Los datos enviados constan de dos partes:

  1. Un entero que indica el tamaño del objeto de mensaje por venir
  2. El objeto de mensaje, algunas NSStrings y un objeto NSData codificado con NSKeyedArchiver

La cuestión:

  1. Cuando el objeto NSData es de alrededor de 1.5Mb, obtengo una excepción de archivo incomprensible cuando intento decodificarlo. Al leer los 4 bytes siguientes a donde debería estar el mensaje, hay un gran número, no el tamaño del siguiente mensaje.
  2. Cuando el objeto NSData tiene alrededor de 80 Kb, dos mensajes se decodifican con éxito y luego recibo la excepción de archivo incomprensible.

Parece que los datos se están descomponiendo en algún momento ... aunque todo el propósito de TCP es mantenerlo en orden. Entonces, ¡debo ser el problema!

Código relevante

Servidor

sendData: se pasa el objeto Message que se ha codificado con NSKeyedArchiver. Se necesita 100 mensajes en un corto período de tiempo

// dataQueue is an NSMutableArray - (void) sendData:(NSData *)data { int size = data.length; NSData *msgSize = [NSData dataWithBytes:&size length:sizeof(int)]; if (self.outputStream.hasSpaceAvailable && (self.dataQueue.count == 0)) { [self.dataQueue addObject:data]; [self.outputStream write:msgSize.bytes maxLength:msgSize.length]; } else { [self.dataQueue addObject:msgSize]; [self.dataQueue addObject:data]; } } //called by NSStreamDelegate method when space is available - (void) hasSpaceAvailable { if (self.dataQueue.count > 0) { NSData *tmp = [self.dataQueue objectAtIndex:0]; [self.outputStream write:tmp.bytes maxLength:tmp.length]; [self.dataQueue removeObjectAtIndex:0]; } }

Cliente

streamHasBytes: recopila los fragmentos del mensaje y los agrega a self.buffer. Cuando la longitud de self.buffer llega a ser mayor que la longitud del mensaje indicada en los primeros 4 bytes de self.buffer, el objeto Message se analiza ...

//Called by NSStreamDelegate method when bytes are available - (void) streamHasBytes:(NSInputStream *)stream { NSInteger bytesRead; uint8_t buffer[32768]; bytesRead= [stream read:buffer maxLength:sizeof(buffer)]; if (bytesRead == -1 || bytesRead == 0) //...err @synchronized(self) { //added to test concurrency [self.buffer appendBytes:buffer length:bytesRead]; } [self checkForMessage]; } - (void) checkForMessage { @synchronized(self) { //added to test concurrency int msgLength = *(const int *)self.buffer.bytes; if (self.buffer.length < msgLength) return; //remove the integer from self.buffer [self.buffer replaceBytesInRange:NSMakeRange(0, sizeof(int)) withBytes:NULL length:0]; //copy the actual message from self.buffer NSData *msgData = [NSData dataWithBytes:self.buffer.bytes length:msgLength]; //remove the message from self.buffer [self.buffer replaceBytesInRange:NSMakeRange(0, msgLength) withBytes:NULL length:0]; Message *theMsg = [NSKeyedUnarchiver unarchiveObjectWithData:msgData]; [self.delegate didReceiveMessage:theMsg]; } }

EDITAR:

Ahora me doy cuenta de que, en el caso donde el objeto NSData en el primer mensaje es de alrededor de 1.5Mb, para un tamaño total de Mensaje de aproximadamente 1.6Mb, el cliente solo recibe 1.3Mb ... Esto explicaría lo incomprensible errores de archivo ¿Por qué no se entregarían todos los datos?


Resulta que, en algunos casos, solo una parte de los datos que supuse que estaba enviando se enviaban en realidad. NSOutputStream write:maxLength: devuelve la cantidad de bytes que se escribieron realmente en la secuencia. Por lo tanto, el método hasSpaceAvailable anterior se puede arreglar con

NSInteger i = [self.outputStream write:tmp.bytes maxLength:tmp.length]; if (i < tmp.length) { //send the difference }


Estoy haciendo algo prácticamente igual, pero fui con un método un poco diferente. Elegí obtener el tamaño del dataToSend archivado, luego convertí la variable de tamaño int a un fragmento mutableData. A continuación, anexo el dataToSend al fragmento de tamaño de datos, creando un fragmento de datos que puede manejar y se "concatena" para enviarlos juntos. De esa forma, todo permanece unido. Se supone que las cosas permanecen juntas, y lo hacen. Tendrá que ajustar los búferes en consecuencia. Tengo datos de entre 400 y 450 bytes, así que tengo un buffer de 512 bytes para el dataToSend, luego el servidor y los clientes tienen 4096 (4K) buffer para acumular los datos enviados. Pero nunca pasa de 1536 bytes con 4 jugadores que comparten datos a 30 fps ...

Cada jugador llama:

- (void)sendData:(CAKNodeSpec *)sentSpec { NSMutableData *archivedData = [NSMutableData dataWithCapacity:512]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archivedData]; [sentSpec encodeWithCoder:archiver]; [archiver finishEncoding]; [self.gameClient outputData:archivedData]; // gameClient is custom NSStreamDelegate with IN/OUT Streams }

En gameClient, y gameConnect para gameServer

- (void)outputData:(NSMutableData *)dataToSend { if (self.outputBuffer != nil) { unsigned int size = (unsigned int)dataToSend.length; // maintains 32-bit/64-bit architecture compatibility & silence warnings NSMutableData *dataSized = [NSMutableData dataWithBytes:&size length:sizeof(unsigned int)]; [dataSized appendBytes:dataToSend.bytes length:size]; [self.outputBuffer appendData:dataSized]; [self startOutput]; } } - (void)startOutput { NSInteger actuallyWritten = [self.outputStream write:self.outputBuffer.bytes maxLength:self.outputBuffer.length]; if (actuallyWritten > 0) { [self.outputBuffer replaceBytesInRange:NSMakeRange(0, (NSUInteger) actuallyWritten) withBytes:NULL length:0]; } else { [self closeStreams]; } }

siguiendo el clásico switch-case-model

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent { assert(aStream == self.inputStream || aStream == self.outputStream); switch(streamEvent) { case NSStreamEventOpenCompleted: { if (aStream == self.inputStream) { self.inputBuffer = [[NSMutableData alloc] init]; self.dataQueue = [[NSMutableArray alloc] init]; } else { self.outputBuffer = [[NSMutableData alloc] init]; } } break; case NSStreamEventHasSpaceAvailable: { if ([self.outputBuffer length] != 0) { [self startOutput]; } } break; case NSStreamEventHasBytesAvailable: { uint8_t buffer[4096]; NSInteger actuallyRead = [self.inputStream read:buffer maxLength:4096]; if (actuallyRead > 0) { [self.inputBuffer appendBytes:buffer length:(NSUInteger)actuallyRead]; NSLog(@"Read: %ld", (long)actuallyRead); [self chopaChunk]; NSMutableData *specData; while ((specData = [self.dataQueue shift])) { [self.delegate handleData:specData]; // unarchives } } else { NSLog(@"empty buffer!"); } } break; case NSStreamEventErrorOccurred: case NSStreamEventEndEncountered: { [self closeStreams]; } break; default: break; } }

Esta es el área de diferencia real:

- (void)chopaChunk { unsigned int dataSize = *(const unsigned int *)self.inputBuffer.bytes; while (self.inputBuffer.length >= (sizeof(unsigned int) + dataSize)) { //remove the integer from self.inputBuffer [self.inputBuffer replaceBytesInRange:NSMakeRange(0, sizeof(unsigned int)) withBytes:NULL length:0]; //copy the actual message from self.inputBuffer NSMutableData *specData = [NSMutableData dataWithBytes:self.inputBuffer.bytes length:dataSize]; [self.dataQueue addObject:specData]; //remove the message from self.inputBuffer [self.inputBuffer replaceBytesInRange:NSMakeRange(0, dataSize) withBytes:NULL length:0]; if (self.inputBuffer.length > 0) { dataSize = *(const unsigned int *)self.inputBuffer.bytes; // I just keep going adding, I guess you would add multiple 80K pieces here } } }

Siguiendo su ejemplo, elijo comprobar si el inputBuffer es> = dataSize + (sizeof (unsigned int)), de esa manera, como un fabricante de salchichas, ¡simplemente deshacemos las piezas a medida que entran! :RE

El flujo es datos para outputBuffer a outputStream <- send -> a inputStream para inputBuffer volver a los datos.

Creo que si usa los datos concatenados en lugar de tratar de enviar dos objetos diferentes, se sentirá mejor que tratar de descubrir quién es quién y cuándo obtendrá qué ... y> = en lugar de <, y simplemente agregue bloques mutableData para su dataQueue ... su (s) caso (s) de uso pueden diferir ...