我正试图通过NSData
向Bluetooth
发送一些GameKit
。
虽然我已经设置GameKit
并且能够发送小消息,但我现在想要扩展并发送整个文件。
我一直在读,你必须先将大文件分成数据包,然后再单独发送。
所以我决定创建一个struct
,以便在另一端收到数据包时更容易解码数据包:
typedef struct {
const char *fileName;
NSData *contents;
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 *)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设置为1,控制台上的输出也是packetNumber = 0
。
我错过了明显的事吗?
我对NSData
或GameKit
了解不多。
所以我的问题是 - 我可以在file_packet
中添加NSData
,如果是,我该如何成功完成 - 以及如何将文件分成多个数据包?
答案 0 :(得分: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
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是通过蓝牙在设备之间传输文件的最佳方式,那么可以预期API将有一种方法可以做到这一点,但它有8K的限制。
我并不是故意隐晦 - 我不知道你的情况会是什么样的合适的API,但是烹饪这个Packet课程让我思考,“必须有更好的方法。” / p>
希望这有帮助。
答案 1 :(得分:4)
您创建的NSData大小为 sizeof(数据包),这只是指针的大小。将其更改为sizeof(file_packet)。
顺便说一下,你并没有真正发送文件名和内容。只有指向它们的指针。