Отправлять и получать NSData через GameKit

Я пытаюсь отправить 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, даже если я установил packetNumber равным 1.

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

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


person Jack Greenhill    schedule 29.01.2011    source источник


Ответы (2)


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

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

Сложная часть процесса пакетирования с использованием 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

Пакет.м:

#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 заставило меня подумать: «Должен быть способ получше».

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

person ipmcc    schedule 29.01.2011
comment
Спасибо за ответ, однако после тестирования он, похоже, дает сбой (EXC_BAD_ACCESS). Не знаю, как это реализовать, потому что то, как я это делаю, не работает - person Jack Greenhill; 30.01.2011
comment
Прочитав ваш комментарий, я обнаружил, что автоматически выпущенный объект неправильно используется. Вы можете попробовать еще раз. Но в целом EXC_BAD_ACCESS часто взаимодействует с освобожденным объектом. Вы можете использовать профиль распределения инструментов с включенными зомби, чтобы выследить их. Это бесценно! - person ipmcc; 30.01.2011
comment
Я слышал, что должен быть способ получше. Я еще ни одного не нашел. Было бы здорово, если бы у кого-то было что-то получше. - person Mr Rogers; 17.05.2012

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

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

person Felix    schedule 29.01.2011