通过 GameKit 发送和接收 NSData [英] Send and receive NSData via GameKit

查看:18
本文介绍了通过 GameKit 发送和接收 NSData的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过 GameKit 通过 Bluetooth 发送一些 NSData.

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

我一直在读到,您必须将大文件拆分成数据包,然后再单独发送.

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

typedef struct {const char *文件名;NSData *内容;整数文件类型;int 数据包编号;int totalpackets;} 文件包;

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

所以对于一个数据包,我想我可以创建一个 file_packet,设置它的属性,然后通过 -sendDataToAllPeers:withDataMode:error 发送它:

NSData *fileData;file_packet *packet = (file_packet *)malloc(sizeof(file_packet));packet->fileName = [文件名 cStringUsingEncoding:NSASCIIStringEncoding];packet->contents = [NSData dataWithContentsOfFile:selectedFilePath];数据包->数据包编号 = 1;packet->totalpackets = 1;数据包->文件类型= 56;//txt文件fileData = [NSData dataWithBytes:(const void *)packet length:sizeof(file_packet)];免费(包);NSError *error = nil;[self.connectionSession sendDataToAllPeers:fileData withDataMode:GKSendDataReliable 错误:&error];如果(错误){NSLog(@"发生错误:%@", [error localizedDescription]);}

但是,我不认为设置 fileData 是正确的 - 并且 error 没有显示任何内容.

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

file_packet *recievedPacket = (file_packet *)malloc(sizeof(file_packet));recievedPacket = (file_packet *)[数据字节];NSLog(@"packetNumber = %d", recievedPacket->packetnumber);...

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

我是否错过了显而易见的事情?我对 NSDataGameKit 了解不多.

所以我的问题是 - 我可以在 NSData 中添加一个 file_packet,如果是,我如何成功 - 以及如何将文件拆分为多个包?

解决方案

添加:

这里你应该做的是创建一个 NSObject 子类来表示你的数据包,然后采用 NSCoding 将它以你想要的方式序列化为一个 NSData.使用 struct 执行此操作不会给您带来任何好处,而且会使事情变得更加困难.它也很脆弱,因为将结构打包到 NSData 中并没有考虑字节序等.

使用 NSCoding 打包过程的棘手部分是你并不真正知道编码过程的开销是多少,所以尽可能大,但仍然在最大数据包大小下是棘手的......

我在没有测试或保修的情况下展示了这个,但如果你想在这种方法上有一个不错的开始,可能就是这样.请注意,我没有检查我的任意 100 个字节的开销是否现实.您必须稍微玩弄数字.

数据包.h:

 @interface Packet : NSObject {NSString* 文件名;NSInteger 文件类型;NSUInteger totalPackets;NSUInteger packetIndex;NSData* 数据包内容;}@property (readonly, copy) NSString* fileName;@property (readonly,assign) NSInteger fileType;@property (readonly,assign) NSUInteger totalPackets;@property (readonly,assign) NSUInteger packetIndex;@property(只读,保留)NSData* packetContents;+ (NSArray*)packetsForFile: (NSString*)name ofType: (NSInteger)type withData: (NSData*)fileContents;@结尾

数据包.m:

#import "Packet.h"@接口包()@property (readwrite,assign) NSUInteger totalPackets;@property(读写,保留)NSData* packetContents;@结尾@实现包- (id)initWithFileName: (NSString*)pFileName ofType: (NSInteger)pFileType index: (NSUInteger)pPacketIndex{if (self = [超级初始化]){文件名 = [pFileName 副本];文件类型 = pFileType;packetIndex = pPacketIndex;totalPackets = NSUIntegerMax;packetContents = [[NSData alloc] init];}回归自我;}- (void)dealloc{[文件名发布];[包内容发布];[超级dealloc];}@合成文件名;@合成文件类型;@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 = [超级初始化]){文件名 = [[aDecoder decodeObjectForKey: @"fileName"] 复制];fileType = [aDecoder decodeInt64ForKey:@"fileType"];totalPackets = [aDecoder decodeInt64ForKey:@"totalPackets"];packetIndex = [aDecoder decodeInt64ForKey:@"packetIndex"];packetContents = [[aDecoder decodeObjectForKey:@"totalPackets"] 保留];}回归自我;}+ (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];//找出非数据包有效载荷有多大...NSMutableData* 数据 = [NSMutableData 数据];NSKeyedArchiver* coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];[第一个encodeWithCoder:编码器];[编码器完成编码];const NSUInteger nonPayloadSize = [数据长度];NSMutableArray* 数据包 = [NSMutableArray 数组];NSUInteger bytesArchived = 0;while (bytesArchived <[文件内容长度]){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 = 有效载荷;bytesArchived += [有效载荷长度];[数据包添加对象:nextPacket];}for (Packet* 包中包){packet.totalPackets = packet.count;}返回数据包;}- (NSData*)dataForSending{NSMutableData* 数据 = [NSMutableData 数据];NSKeyedArchiver* coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];[self encodeWithCoder: 编码器];[编码器完成编码];返回 [NSData dataWithData:data];}+ (Packet*)packetObjectFromRxdData:(NSData*)data{NSKeyedUnarchiver* 解码器 = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease];return [[[Packet alloc] initWithCoder:decoder] autorelease];}@结尾

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

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

我不是故意神秘——我不知道适合您的情况的正确 API 是什么,但是编写这个 Packet 类的练习让我想,一定有更好的方法."

希望这会有所帮助.

I'm trying to send some NSData over Bluetooth through GameKit.

While I've got GameKit set up and are able to send small messages across, I now would like to expand and send across whole files.

I've been reading that you have to split large files up into packets before sending them across individually.

So I decided to create a struct to make it easier to decode the packets when they're received at the other end:

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

However, for small files (8KB and less) I thought one packet will be enough.

So for one packet, I thought I would be able to create a file_packet, set its properties, and send it via -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]);
}

However, I don't think something's right setting fileData - and error displays nothing.

When a file's received, I do the following:

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

However, the output on the console is packetNumber = 0, even when I set packetNumber to 1.

Am I missing the obvious? I don't know much about NSData or GameKit.

So my question is - Can I add a file_packet in NSData, and if so, How do I do it successfully - and How do you split files up into multiple packets?

解决方案

To add on:

What you ought to do here is make an NSObject subclass to represent your packet, and then adopt NSCoding to serialize it to an NSData in the way that you want. Doing this with a struct isn't buying you anything, and makes things even harder. It's also fragile, since packing a struct into an NSData doesn't account for things like endian-ness, etc.

The tricky part of the packetizing process using NSCoding is that you don't really know what the overhead of the coding process is, so being as big as possible, but still under the max packet size is tricky...

I present this without testing, or warranty, but if you want a decent start on that approach, this may be it. Be warned, I didn't check to see if my arbitrary 100 bytes for overhead was realistic. You'll have to play with the numbers a little bit.

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

The reassemblage of the original file from these packets can be done using much the same approach as splitting it up... Iterate over the packets, copying from the individual packet payload NSDatas into a big NSMutableData.

In closing, I feel compelled to say that when you find yourself doing something like this, that boils down to implementing a primitive TCP stack, it's usually time to stop yourself and ask if there aren't better ways to do this. Put differently, if GameKit were the best way to transfer files between devices over bluetooth, one would expect that the API would have a method for doing just that, but instead it has this 8K limit.

I'm not being intentionally cryptic -- I don't know what the right API would be for your situation, but the exercise of cooking up this Packet class left me thinking, "there's gotta be a better way."

Hope this helps.

这篇关于通过 GameKit 发送和接收 NSData的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆