2011-01-29 11 views
5

Próbuję wysłać niektóre NSData przez Bluetooth do GameKit.Wysyłaj i odbieraj dane NSData za pośrednictwem GameKit

Podczas gdy mam GameKit skonfigurować i są w stanie wysyłać małe wiadomości przez, teraz chciałbym rozwinąć i wysłać na całe pliki.

Czytałem, że musisz podzielić duże pliki na pakiety przed wysłaniem ich pojedynczo.

Więc postanowiłem stworzyć struct aby ułatwić dekodowanie pakietów, gdy są one otrzymane na drugim końcu:

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

Jednak dla małych plików (8KB i mniej) Myślałem jeden pakiet to wystarczy.

Więc dla jednego pakietu, myślałem, że będę w stanie stworzyć file_packet ustawić jego właściwości, i wysłać go poprzez -sendDataToAllPeers: withDataMode: błąd:

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

Jednak nie sądzę, że coś jest prawidłowe ustawienie fileData - i error nic nie wyświetla.

Gdy plik otrzymali, mam następujące:

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

Jednak wyjście na konsoli jest packetNumber = 0, nawet kiedy ustawić packetNumber do 1.

jestem brakuje oczywiste? Niewiele wiem o NSData lub GameKit.

Moje pytanie brzmi - czy mogę dodać file_packet w NSData, a jeśli tak, to jak mam to zrobić - i jak podzielić pliki na wiele pakietów?

Odpowiedz

8

Aby dodać na:

Co powinieneś zrobić tutaj jest zrób podklasa NSObject reprezentować swój pakiet, a następnie przyjąć NSCoding serializować go do NSData w sposób, który chcesz. Robiąc to za pomocą struct nie kupujesz niczego i sprawiasz, że jest jeszcze trudniej. Jest również delikatny, ponieważ pakowanie struktury do NSData nie uwzględnia rzeczy takich jak endian-ness, itp.

Trudną częścią procesu pakietowania przy użyciu NSCoding jest to, że tak naprawdę nie wiesz, co narzut proces kodowania jest tak duży, jak to możliwe, ale wciąż pod maksymalnym rozmiarem pakietu jest trudny ...

Przedstawiam to bez testowania lub gwarancji, ale jeśli chcesz przyzwoicie rozpocząć to podejście, może to być to. Uważaj, nie sprawdziłem, czy moje arbitralne 100 bajtów dla obciążenia jest realistyczne. Będziesz musiał grać trochę z liczbami.

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

reassemblage oryginalnego pliku z tych pakietów może być wykonane przy użyciu znacznie takie samo podejście jak dzielenie go ... iteracyjne nad pakietami, kopiowanie z pojedynczego pakietu NSDatas z pakietem danych do dużej NSMutableData.

Podsumowując, czuję się zmuszony powiedzieć, że kiedy robisz coś takiego, to sprowadza się do implementacji prymitywnego stosu TCP, to zwykle czas, aby zatrzymać się i zapytać, czy nie ma lepszych sposobów, aby to zrobić. . Inaczej mówiąc, jeśli GameKit byłby najlepszym sposobem przesyłania plików między urządzeniami za pośrednictwem bluetooth, można by oczekiwać, że API będzie miało taką metodę, ale zamiast tego ma ten limit 8K.

Nie jestem intencjonalnie tajemniczy - nie wiem, jakie było właściwe API dla twojej sytuacji, ale ćwiczenie gotowania tej klasy Packet sprawiło, że pomyślałem: "musi być lepsza droga".

Mam nadzieję, że to pomoże.

+0

Dziękuję za odpowiedź, jednak po przetestowaniu wydaje się, że zawiesił się (EXC_BAD_ACCESS). Nie wiem, jak to zaimplementować, ponieważ sposób, w jaki to robię, nie działa. –

+0

Po przeczytaniu Twojego komentarza znalazłem złe użycie autoreleased obiektu. Możesz spróbować jeszcze raz. Ogólnie rzecz biorąc, EXC_BAD_ACCESS często "wchodzi w interakcje z deallokowanym obiektem". Możesz użyć profilu Przydziałów Instrumentów, z włączonymi Zombiesami, aby polować na nich. Jest nieoceniony! – ipmcc

+0

Po kilku zmianach kod działa idealnie. Dzięki. –

4

Tworzysz NSData o rozmiarze sizeof (pakiet), który jest tylko wskaźnikiem rozmiaru. Zmień go na sizeof (file_packet).

BTW, tak naprawdę nie wysyłasz nazwy pliku i zawartości. Tylko wskazówki do nich.