2011-01-29 5 views
5

Sto cercando di inviare alcune NSData oltre Bluetooth attraverso GameKit.Inviare e ricevere NSData via GameKit

Mentre ho impostato GameKit e sono in grado di inviare messaggi di piccole dimensioni, ora mi piacerebbe espandere e inviare su interi file.

Ho letto che si deve dividere file di grandi dimensioni in pacchetti prima di inviarli attraverso singolarmente.

Così ho deciso di creare un struct per rendere più facile per decodificare i pacchetti quando sono ricevuti all'altro capo:

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

Tuttavia, per file di piccole dimensioni (8 KB e meno) ho pensato un pacchetto sarà abbastanza

Così, per un pacchetto, ho pensato di essere in grado di creare un file_packet, impostarne le proprietà, e inviarlo via -sendDataToAllPeers: withDataMode: errore:

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]); 
} 

Tuttavia, non credo che c'è qualcosa che non right setting fileData - e error non visualizza nulla.

Quando di ricevuta, faccio il seguente file:

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

Tuttavia, l'output sulla console è packetNumber = 0, anche quando ho impostato packetNumber a 1.

Mi sto perdendo l'ovvio? Non so molto su NSData o GameKit.

Quindi la mia domanda è - Posso aggiungere uno file_packet in NSData e, in caso affermativo, Come faccio a farlo con successo - e Come dividi i file in più pacchetti?

risposta

8

Per aggiungere il:

Che cosa si dovrebbe fare qui è fare una sottoclasse NSObject per rappresentare il pacchetto, e quindi adottare NSCoding per serializzare a un NSData nel modo in cui si desidera. Fare questo con una struttura non ti sta comprando nulla e rende le cose ancora più difficili. È anche fragile, dal momento che l'imballaggio di una struct in un NSData non tiene conto di cose come endianità, ecc.

La parte difficile del processo di pacchettizzazione utilizzando NSCoding è che non si sa veramente quale sia il sovraccarico del il processo di codifica è, quindi, essere il più grande possibile, ma ancora sotto la dimensione massima del pacchetto è ingannevole ...

Presente questo senza test, o garanzia, ma se si desidera un avvio decente su tale approccio, questo può essere esso. Stai attento, non ho verificato se i miei 100 byte arbitrari per il sovraccarico fossero realistici. Dovrai giocare un po 'con i numeri.

Packet.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 

La ricomposizione del file originale da questi pacchetti può essere fatto utilizzando più o meno lo stesso approccio di dividere in su ... iterare i pacchetti, la copia dal singolo payload del pacchetto NSDatas in un grande NSMutableData.

In chiusura, mi sento in dovere di dire che quando ti ritrovi a fare qualcosa del genere, che si riduce all'implementazione di uno stack TCP primitivo, di solito è il momento di fermarti e chiedere se non ci sono modi migliori per farlo . In altre parole, se GameKit fosse il modo migliore per trasferire file tra dispositivi tramite bluetooth, ci si aspetterebbe che l'API avesse un metodo per farlo, ma invece ha questo limite di 8K.

Non sono intenzionalmente criptico - non so quale sarebbe l'API giusta per la tua situazione, ma l'esercizio di cucinare questa classe Packet mi ha lasciato pensare: "ci deve essere un modo migliore".

Spero che questo aiuti.

+0

Grazie per la risposta, tuttavia, dopo averlo testato, sembra bloccarsi (EXC_BAD_ACCESS). Non so come implementarlo perché il modo in cui sto facendo non funziona –

+0

Dopo aver letto il tuo commento, ho trovato un cattivo uso di un oggetto autoreleased. Puoi provare di nuovo. Ma in generale, EXC_BAD_ACCESS è spesso "interagito con un oggetto dealloced". Puoi usare il profilo di Allocations di Strumenti, con gli Zombi accesi, per cacciare quelli verso il basso. È inestimabile! – ipmcc

+0

Dopo alcune regolazioni il codice funziona perfettamente. Grazie. –

4

Si crea NSData con dimensione sizeof (pacchetto), che è solo la dimensione del puntatore. Cambialo in sizeof (file_packet).

BTW, non sei davvero l'invio del nome del file e il contenuto. Solo le indicazioni per loro.