【问题标题】:Send and receive NSData via GameKit通过 GameKit 发送和接收 NSData
【发布时间】:2023-03-17 12:17:01
【问题描述】:

我正在尝试通过GameKitBluetooth 发送一些NSData

虽然我已经设置了 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

我错过了显而易见的事情吗? 我不太了解NSDataGameKit

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

【问题讨论】:

    标签: iphone objective-c data-structures nsdata gamekit


    【解决方案1】:

    补充:

    你应该在这里做一个 NSObject 子类来表示你的数据包,然后采用 NSCoding 以你想要的方式将它序列化为一个 NSData。用结构做这件事不会给你买任何东西,而且会让事情变得更加困难。它也很脆弱,因为将结构打包到 NSData 中并没有考虑字节序等问题。

    使用 NSCoding 打包过程的棘手部分是您并不真正知道编码过程的开销是多少,因此尽可能大,但仍低于最大数据包大小是棘手的......

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

    数据包.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
    

    数据包.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 类的练习让我想,“一定有更好的方法。”

    希望这会有所帮助。

    【讨论】:

    • 感谢您的回答,但是,经过测试,它似乎崩溃了(EXC_BAD_ACCESS)。不知道如何实现它,因为我这样做的方式不起作用
    • 阅读您的评论后,我发现自动释放对象的使用不当。您可能想再试一次。但一般来说,EXC_BAD_ACCESS 通常是“与解除分配的对象交互”。您可以使用 Instruments 的 Allocations 配置文件,打开 Zombies 来追捕那些。这是无价的!​​span>
    • 我听到你说“肯定有更好的方法”。我还没有找到一个。如果有人有更好的东西,那就太棒了。
    【解决方案2】:

    您创建大小为 sizeof(packet) 的 NSData,它只是指针的大小。将其更改为 sizeof(file_packet)。

    顺便说一句,您并没有真正发送文件名和内容。只有指向它们的指针。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多