通过GameKit发送和接收NSData

Jac*_*ill 5 iphone objective-c nsdata gamekit data-structures

我想送一些NSDataBluetooth通过GameKit.

虽然我已经GameKit设置好并且能够发送小消息,但我现在想要扩展并发送整个文件.

我一直在读你必须将大文件分成数据包然后单独发送它们.

所以我决定创建一个struct以便在另一端收到数据包时更容易解码数据包:

typedef struct {
    const char *fileName;
    NSData *contents;
    int fileType;
 int packetnumber;
 int totalpackets;
} file_packet; 
Run Code Online (Sandbox Code Playgroud)

但是,对于小文件(8KB或更少),我认为一个数据包就足够了.

所以对于一个数据包,我想我可以创建一个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]);
}
Run Code Online (Sandbox Code Playgroud)

但是,我不认为设置fileData是正确的 - 并且error什么都不显示.

收到文件后,我会执行以下操作:

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

但是,packetNumber = 0即使我将packetNumber设置为1,控制台上的输出也是如此.

我错过了明显的吗?我不太了解NSDataGameKit.

所以我的问题是-我可以添加file_packetNSData,如果是这样,我该怎么做它成功-你怎么划分的文件分成多个数据包?

ipm*_*mcc 8

添加:

你应该做的是创建一个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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

从这些数据包中重新组合原始文件可以使用与拆分它相同的方法完成...迭代数据包,从单个数据包有效负载NSDatas复制到一个大的NSMutableData.

最后,我觉得有必要说,当你发现自己做这样的事情时,归结为实现一个原始的TCP堆栈,通常是时候停止自己并询问是否有更好的方法来做到这一点.换句话说,如果GameKit是通过蓝牙在设备之间传输文件的最佳方式,那么可以预期API将有一种方法可以做到这一点,但它有8K的限制.

我并没有故意隐晦 - 我不知道适合你情况的API是什么,但是烹饪这个Packet类的练习让我想到,"必须有更好的方法."

希望这可以帮助.