Отправка и получение NSData через GameKit - PullRequest
5 голосов
/ 29 января 2011

Я пытаюсь отправить NSData через Bluetooth через GameKit.

Пока я настроил GameKit и могу отправлять небольшие сообщения, теперь я хотел бы расширить и отправить целые файлы.

Я читал, что вы должны разбивать большие файлы на пакеты, прежде чем отправлять их по отдельности.

Поэтому я решил создать struct, чтобы упростить декодирование пакетов при их получении на другом конце:

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

Однако для небольших файлов (8 КБ и менее) я подумал, что одного пакета будет достаточно.

Так что для одного пакета я подумал, что смогу создать file_packet, установить его свойства и отправить его через -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]);
}

Однако я не думаю, что что-то правильно настраивает fileData - и error ничего не отображает.

Когда файл получен, я делаю следующее:

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

Однако на консоли выводится packetNumber = 0, даже когда я устанавливаю номер_пакета равным 1.

Я упускаю очевидное? Я не знаю много о NSData или GameKit.

Так что мой вопрос - могу ли я добавить file_packet в NSData, и если да, как мне это сделать успешно - и как разбить файлы на несколько пакетов?

Ответы [ 2 ]

8 голосов
/ 29 января 2011

Чтобы добавить:

То, что вы должны здесь сделать, - это создать подкласс NSObject для представления вашего пакета, а затем принять NSCoding для сериализации его в NSData желаемым способом.Выполнение этого со структурой ничего не покупает, а усложняет.Это также хрупко, так как упаковка структуры в NSData не учитывает такие вещи, как endian-ness и т. Д.

Сложная часть процесса пакетирования с использованием NSCoding заключается в том, что вы на самом деле не знаете, что такое издержкипроцесса кодирования, поэтому быть настолько большим, насколько это возможно, но все же при максимальном размере пакета сложно, *

Я представляю это без тестирования или гарантии, но если вы хотите достойный старт на этом подходеэто может быть это.Будьте предупреждены, я не проверял, были ли мои произвольные 100 байтов для служебных данных реалистичными.Вам придется немного поиграть с числами.

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

Повторная сборка оригиналафайл из этих пакетов можно сделать, используя тот же подход, что и его разбиение ... Перебирайте пакеты, копируя из отдельных полезных данных пакета NSDatas в большой NSMutableData.

В заключение я вынужден сказать, чточто когда вы обнаруживаете, что делаете что-то подобное, сводящееся к реализации примитивного стека TCP, обычно пора остановиться и спросить, нет ли лучшего способа сделать это.Иными словами, если бы GameKit был лучшим способом передачи файлов между устройствами по Bluetooth, можно было бы ожидать, что у API был бы метод для этого, но вместо этого у него было бы ограничение в 8 КБ.

Я небыть намеренно загадочным - я не знаю, какой API подойдет для вашей ситуации, но упражнение по приготовлению этого класса Packet заставило меня задуматься: «Должен быть лучший путь».

Надеюсь, этопомогает.

4 голосов
/ 29 января 2011

Вы создаете NSData с размером sizeof (пакет) , который является только размером указателя.Измените его на sizeof (file_packet).

Кстати, вы на самом деле не отправляете имя файла и содержимое.Только указатели на них.

...