通过UDP传递结构弱键入

时间:2009-11-18 23:32:28

标签: iphone objective-c networking udp

我是网络编程新手,基本上是边做边学。我正在跟随 iPhone酷项目的Mike Ashes程序“Sphere Net”。我理解程序中发生了什么,但是我已经准备好开始扩展它了,我期望发送一种合理的各种数据包类型(每种都表示为结构)。为每种类型的数据包声明不同的方法似乎并不严格。

如果我正在处理objective-c类,我会让函数将超类作为参数,或者使用id。但由于我使用的是按价值结构,我认为我不能使用这种策略。

我要求指向一个很好的参考指针,解释一个典型的程序如何处理各种各样的数据包(struct)类型,无论是书籍,链接还是堆栈溢出答案。

(翻译:我不想写几个完全相同的新方法,除了每次我决定添加新类型数据包时它们处理的数据包类型)

作为参考,Sphere Net类型程序的基本概要在标题中:

typedef struct { 
    uint32_t identifier; 
    uint32_t datatype; 
} PacketHeader; 

typedef struct { 
    PacketHeader header; 
    int32_t dataItem1;
    int32_t dataItem2;
} MyPacket;
static const uint32_t kPacketIdentifier = 'pkt'; 

在实施中:

-(void) init{
// do all the setup, open the sockets, start bonjour.
        // start the listener thread 
        [NSThread detachNewThreadSelector:@selector(listenThread) toTarget:self withObject:nil]; 
}

- (void)objectOfInterestChanged:(ObjectOfInterest *)interestingObject { 

    PositionPacket packet; 

     packet.dataItem1 = CFSwapInt32HostToBig(round(interestingObject.someFloat)); 
     packet.dataItem2 = CFSwapInt32HostToBig(round(interestingObject.someFloat)); 

    [self sendUpdatePacket:packet]; 
} 

- (void)sendUpdatePacket:(MyPacket)packet{ 

    packet.header.identifier = CFSwapInt32HostToBig(kPacketIdentifier); 
    packet.header.datatype = CFSwapInt32HostToBig(kPacketType); 

    for(NSNetService *service in _services) 
        for(NSData *address in [service addresses]) 
            sendto(_socket, &packet, sizeof(packet), 0, [address bytes], [address length]); 
}

- (void)listenThread { 
    while(1) 
    { 
        MyPacket packet; 
        struct sockaddr addr; 
        socklen_t socklen = sizeof(addr); 
        ssize_t len = recvfrom(_socket, &packet, sizeof(packet), 0, &addr,  &socklen); 
        if(len != sizeof(packet)) 
            continue; 
        if(CFSwapInt32BigToHost(packet.basePacket.header.identifier) != kPacketIdentifier)
            continue; 
        //if(CFSwapInt32BigToHost(packet.basePacket.header.datatype) != kPacketType)
        //    continue; 
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
        NSData *packetData = [NSData dataWithBytes:&packet length:sizeof(packet)]; 
        NSData *addressData = [NSData dataWithBytes:&addr length:socklen]; 
        NSArray *arguments = [NSArray arrayWithObjects:packetData, addressData, nil]; 
        //SEL mainThreadSEL = @selector(mainThreadReceivedPositionPacket:); 
        SEL mainThreadSEL; 
        if(CFSwapInt32BigToHost(packet.basePacket.header.datatype) == kPacketType)
            mainThreadSEL = @selector(mainThreadReceivedPacket:); 
        [self performSelectorOnMainThread:mainThreadSEL withObject:arguments waitUntilDone:YES]; 
        [pool release]; 
    } 
} 

- (void)mainThreadReceivedPacket:(NSArray *)arguments { 
    // extract the objects from the array created above 
    NSData *packetData = [arguments objectAtIndex:0]; 
    NSData *addressData = [arguments objectAtIndex:1]; 
    const MyPacket *packet = [packetData bytes]; 

   // ...accounting for differences in endianness 
    int32_t x = CFSwapInt32BigToHost(packet->dataItem1); 
    int32_t y = CFSwapInt32BigToHost(packet->dataItem2); 

    AnObject *update;
    update.interestingUpdate = CGThingMake(x, y);  

    [delegate networkController:self didReceiveUpdate:update fromAddress:addressData]; 
} 

1 个答案:

答案 0 :(得分:0)

经过一番研究后,我拿到了一个解决方案。它基本上是这样的:

使用特定信息创建特定数据包 创建一个“传输”数据包,其结构如下:

typedef struct TransmissionStruct{
  size_t typeOfPacketIdentifier;
  char arrayOfBytesHoldingSpecificStruct [<sizeoflargeststruct>]
} Transmission Struct

然后使用memcopy(const void*, const void*, size);复制transimssion结构的char数组中特定结构的数据。

然后在接收端,你知道你收到的任何字节的第一个字节将是类型标识符,所以读取前四个字节,然后从那里你可以决定如何处理其余的字节

这方面的一个例子是: iphone problem receiving UDP packets