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

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

问题描述

我试图通过 GameKit NSData 超过蓝牙 c $ c>。



虽然我已经设置了 GameKit ,并能够发送小邮件,我现在想扩展并发送整个文件。



我一直在阅读,你必须将大文件分解成数据包,然后才能单独发送。 p>

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

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

但是,对于小文件(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 *)数据包长度:sizeof(file_packet)];
free(packet);

NSError * error = nil;
[self.connectionSession sendDataToAllPeers:fileData withDataMode:GKSendDataReliable error:& error];
if(error){
NSLog(@发生错误:%@,[error localizedDescription]);
}

但是,我不认为有什么是正确的设置fileData - 和错误不显示任何内容。



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

  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



所以我的问题是 - 我可以在 NSData 中添加一个 file_packet ,如果是,我会成功地做到这一点 - 你如何将文件分解成多个数据包?

解决方案

要添加:



您应该在这里做一个NSObject子类来表示您的数据包,然后采用NSCoding将其按照所需的方式序列化为NSData。用一个结构来做这个,不是买东西,而且使事情变得更加困难。它也是脆弱的,因为将结构体打包成NSData不会占用endian-ness等等。



使用NSCoding的打包过程的棘手部分是你真的不知道编码过程的开销是什么,所以尽可能的大,但仍然是最大包大小是棘手的...



我提供这个没有测试或保修,但如果你想要一个体面的开始在这种方法,这可能是它。被警告,我没有检查我的任意100字节的开销是否现实。你必须玩一些数字。



Packet.h:

  @interface包:NSObject< NSCoding> 
{
NSString * fileName;
NSInteger fileType;
NSUInteger totalPackets;
NSUInteger packetIndex;
NSData * packetContents;
}

@property(只读,复制)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类型:(NSInteger)类型withData:(NSData *)fileContents;

@end

Packet.m:

  #importPacket.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];
}
返回自我;


+(NSArray *)packetsForFile:(NSString *)name类型:(NSInteger)类型withData:(NSData *)fileContents
{
const NSUInteger quanta = 8192;

数据包* first = [[[Packet alloc] initWithFileName:name of type:type index:0] autorelease];

//了解NON数据包的有效载荷有多大?
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];
}

(数据包中的数据包)
{
packet.totalPackets = packets.count;
}

返回数据包;
}

- (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是通过蓝牙在设备之间传输文件的最佳方法,那么可以预期该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天全站免登陆