2011-01-29 12 views
5

Estoy tratando de enviar algunos NSData a través de Bluetooth a través de GameKit.Enviar y recibir NSData a través de GameKit

Mientras tengo configurado GameKit y puedo enviar mensajes pequeños, ahora me gustaría expandir y enviar archivos completos.

He estado leyendo que debe dividir archivos grandes en paquetes antes de enviarlos de forma individual.

así que decidí crear un struct para que sea más fácil de decodificar los paquetes cuando son recibidos en el otro extremo:

typedef struct { 
    const char *fileName; 
    NSData *contents; 
    int fileType; 
int packetnumber; 
int totalpackets; 
} file_packet; 

Sin embargo, para archivos pequeños (de 8 KB y menos) pensé un paquete será suficiente.

Así que para un paquete, pensé que iba a ser capaz de crear un file_packet, establecer sus propiedades, y enviarlo por -sendDataToAllPeers: withDataMode: Error:

NSData *fileData; 
file_packet *packet = (file_packet *)malloc(sizeof(file_packet)); 
packet->fileName = [filename cStringUsingEncoding:NSASCIIStringEncoding]; 
packet->contents = [NSData dataWithContentsOfFile:selectedFilePath]; 
packet->packetnumber = 1; 
packet->totalpackets = 1; 
packet->fileType = 56; //txt document 
fileData = [NSData dataWithBytes:(const void *)packet length:sizeof(file_packet)]; 
free(packet); 

NSError *error = nil; 
[self.connectionSession sendDataToAllPeers:fileData withDataMode:GKSendDataReliable error:&error]; 
if (error) { 
NSLog(@"An error occurred: %@", [error localizedDescription]); 
} 

Sin embargo, no creo que algo anda la configuración correcta fileData - y error no muestra nada.

Cuando ha recibido, hago lo siguiente en un archivo:

file_packet *recievedPacket = (file_packet *)malloc(sizeof(file_packet)); 
recievedPacket = (file_packet *)[data bytes]; 
NSLog(@"packetNumber = %d", recievedPacket->packetnumber); 
... 

Sin embargo, la salida de la consola es packetNumber = 0, incluso cuando me puse a packetNumber 1.

Me estoy perdiendo lo obvio? No sé mucho sobre NSData o GameKit.

Así que mi pregunta es: ¿Puedo agregar un file_packet en NSData? De ser así, ¿cómo lo hago con éxito? ¿Cómo se dividen los archivos en múltiples paquetes?

Respuesta

8

Para añadir:

Lo que usted debe hacer aquí es hacer una subclase NSObject para representar su paquete, y luego adoptar NSCoding para serializar a un NSData en la forma en que desea. Hacer esto con una estructura no es comprar nada, y hace que las cosas sean aún más difíciles. También es frágil, ya que empaquetar una estructura en un NSData no tiene en cuenta aspectos como endianidad, etc.

La parte engañosa del proceso de paquetización con NSCoding es que no se sabe realmente la sobrecarga del proceso de codificación es tan grande como sea posible, pero aún bajo el tamaño máximo de paquete es complicado ...

Presento esto sin pruebas o garantía, pero si desea un comienzo decente en este enfoque, esto puede ser eso. Tenga cuidado, no verifiqué si mis 100 bytes arbitrarios para sobrecarga eran realistas. Tendrás que jugar un poco con los números.

Paquete.h:

@interface Packet : NSObject <NSCoding> 
    { 
     NSString* fileName; 
     NSInteger fileType; 
     NSUInteger totalPackets; 
     NSUInteger packetIndex; 
     NSData* packetContents; 
    } 

    @property (readonly, copy) NSString* fileName; 
    @property (readonly, assign) NSInteger fileType; 
    @property (readonly, assign) NSUInteger totalPackets; 
    @property (readonly, assign) NSUInteger packetIndex; 
    @property (readonly, retain) NSData* packetContents; 

    + (NSArray*)packetsForFile: (NSString*)name ofType: (NSInteger)type withData: (NSData*)fileContents; 

@end 

Packet.m:

#import "Packet.h" 

@interface Packet() 

@property (readwrite, assign) NSUInteger totalPackets; 
@property (readwrite, retain) NSData* packetContents; 

@end 

@implementation Packet 

- (id)initWithFileName: (NSString*)pFileName ofType: (NSInteger)pFileType index: (NSUInteger)pPacketIndex 
{ 
    if (self = [super init]) 
    { 
     fileName = [pFileName copy]; 
     fileType = pFileType; 
     packetIndex = pPacketIndex; 
     totalPackets = NSUIntegerMax; 
     packetContents = [[NSData alloc] init]; 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    [fileName release]; 
    [packetContents release]; 
    [super dealloc]; 
} 

@synthesize fileName; 
@synthesize fileType; 
@synthesize totalPackets; 
@synthesize packetIndex; 
@synthesize packetContents; 

- (void)encodeWithCoder:(NSCoder *)aCoder 
{ 
    [aCoder encodeObject: self.fileName forKey: @"fileName"]; 
    [aCoder encodeInt64: self.fileType forKey:@"fileType"]; 
    [aCoder encodeInt64: self.totalPackets forKey:@"totalPackets"]; 
    [aCoder encodeInt64: self.packetIndex forKey:@"packetIndex"]; 
    [aCoder encodeObject: self.packetContents forKey:@"totalPackets"]; 
} 

- (id)initWithCoder:(NSCoder *)aDecoder 
{ 
    if (self = [super init]) 
    {   
     fileName = [[aDecoder decodeObjectForKey: @"fileName"] copy]; 
     fileType = [aDecoder decodeInt64ForKey:@"fileType"]; 
     totalPackets = [aDecoder decodeInt64ForKey:@"totalPackets"]; 
     packetIndex = [aDecoder decodeInt64ForKey:@"packetIndex"]; 
     packetContents = [[aDecoder decodeObjectForKey:@"totalPackets"] retain]; 
    } 
    return self; 
} 

+ (NSArray*)packetsForFile: (NSString*)name ofType: (NSInteger)type withData: (NSData*)fileContents 
{ 
    const NSUInteger quanta = 8192; 

    Packet* first = [[[Packet alloc] initWithFileName:name ofType:type index: 0] autorelease]; 

    // Find out how big the NON-packet payload is... 
    NSMutableData* data = [NSMutableData data]; 
    NSKeyedArchiver* coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease]; 
    [first encodeWithCoder: coder]; 
    [coder finishEncoding]; 

    const NSUInteger nonPayloadSize = [data length]; 

    NSMutableArray* packets = [NSMutableArray array]; 
    NSUInteger bytesArchived = 0; 
    while (bytesArchived < [fileContents length]) 
    { 
     Packet* nextPacket = [[[Packet alloc] initWithFileName: name ofType: type index: packets.count] autorelease]; 
     NSRange subRange = NSMakeRange(bytesArchived, MIN(quanta - nonPayloadSize - 100, fileContents.length - bytesArchived)); 
     NSData* payload = [fileContents subdataWithRange: subRange]; 
     nextPacket.packetContents = payload; 
     bytesArchived += [payload length];   
     [packets addObject: nextPacket]; 
    } 

    for (Packet* packet in packets) 
    { 
     packet.totalPackets = packets.count; 
    } 

    return packets; 
} 

- (NSData*)dataForSending 
{ 
    NSMutableData* data = [NSMutableData data]; 
    NSKeyedArchiver* coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease]; 
    [self encodeWithCoder: coder]; 
    [coder finishEncoding]; 
    return [NSData dataWithData:data]; 
} 

+ (Packet*)packetObjectFromRxdData:(NSData*)data 
{ 
    NSKeyedUnarchiver* decoder = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease]; 
    return [[[Packet alloc] initWithCoder:decoder] autorelease]; 
} 

@end 

El Reassemblage del archivo original de estos paquetes se puede hacer usando tanto el mismo enfoque que dividirlo ... iterar sobre los paquetes, copia desde la carga útil del paquete individual NSDatas a un gran NSMutableData.

Para terminar, me siento obligado a decir que cuando te encuentras haciendo algo como esto, se reduce a la implementación de una pila TCP primitiva, generalmente es hora de detenerte a ti mismo y preguntar si hay formas mejores de hacerlo. . Dicho de otra manera, si GameKit fuera la mejor manera de transferir archivos entre dispositivos a través de bluetooth, uno esperaría que la API tuviera un método para hacer exactamente eso, pero en cambio tiene este límite de 8 KB.

No estoy siendo intencionalmente críptico. No sé cuál sería la API correcta para su situación, pero el ejercicio de cocinar esta clase de Paquetes me hizo pensar: "tiene que haber una manera mejor".

Espero que esto ayude.

+0

Gracias por su respuesta, sin embargo, después de probarlo, parece bloquearse (EXC_BAD_ACCESS). No estoy seguro de cómo implementarlo porque la forma en que lo estoy haciendo no funciona –

+0

Después de leer su comentario, encontré un mal uso de un objeto liberado automáticamente. Es posible que desee intentarlo de nuevo. Pero en general, EXC_BAD_ACCESS a menudo "interactúa con un objeto desasignado". Puedes usar el perfil de Asignaciones de Instrumentos, con Zombies activados, para cazarlos. ¡Es invaluable! – ipmcc

+0

Después de algunos ajustes, el código funciona perfectamente. Gracias. –

4

Crea el NSData con el tamaño sizeof (paquete), que es solo el tamaño del puntero. Cámbielo a sizeof (file_packet).

Por cierto, realmente no está enviando el nombre de archivo y el contenido. Solo los consejos para ellos.

Cuestiones relacionadas