2

谁能帮我?我使用 GCDAsyncSocket 通过 TCP 协议在两个设备之间集中交换数据。我发送这样的数据:

     NSMutableDictionary *packet = [[[NSMutableDictionary alloc] init] autorelease];
     [packet setObject:[NSNumber numberWithInt:MultiPlayerTypeInfoNextRoundConfirm] forKey:@"type_info"];
     [packet setObject:[NSNumber numberWithBool:YES] forKey:@"connection_confirmation"];
     NSMutableData *data = [[NSMutableData alloc] initWithData:[NSKeyedArchiver archivedDataWithRootObject:packet]]; //[NSKeyedArchiver archivedDataWithRootObject:packet];

     if (currentGameMode == GameModeServer)
        [(ServerMultiplayerManager *)multiplayerManager sendNetworkPacket:data withTag:MultiPlayerTypeInfoNextRoundConfirm];

- (void)sendNetworkPacket:(NSData *)data withTag:(long)tag
{
[asyncSocket writeData:data withTimeout:-1 tag:tag];
}

- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
   NSLog(@"DID WRITE DATA tag is %ld", tag);

   [sock readDataWithTimeout:-1 tag:0];
}

我读到这样的数据:

- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
 NSString *receivedInfo = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];

 [info_data setData:data];

NSLog(@"DID READ DATA WITH TAG %ld", tag);

if ([receivedInfo isEqualToString:ROOM_FILLED])
{
   isMaster = (tcpRequest.identifier == MASTER_CHAR);
    NSLog(@"IS MASTER SET %d", isMaster);

   [multiplayerDelegate setGameModeServer];
   [multiplayerDelegate startGame];
}
else
   [self dataProcessing:info_data];

[sender readDataWithTimeout:-1 tag:0];
}

- (void)dataProcessing:(NSData *)data
 { 
   NSDictionary        *dict       = [NSKeyedUnarchiver unarchiveObjectWithData:data];
  MultiPlayerTypeInfo  typeInfo   = [[dict objectForKey:@"type_info"] intValue];
}

我的问题是这些数据包被弄乱了。假设一个标有标签 10 的数据包在接收器设备上被读取为标有标签 11 的数据包,该数据包是在数据包 10 之后立即发送的,并且在取消归档实际数据包 11 时NSKeyedUnarchiver会抛出异常Incomprehensible archive

据我了解,我应该以某种方式分离数据包。我尝试将分隔符号附加到正在发送的数据中:

[data appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];

并尝试像这样阅读它:

[socket readDataToData:[GCDAsyncSocket CRLFData] timeout:-1 tag:some_tag];

但这没有帮助。我做错了什么,我应该怎么做?

4

1 回答 1

8

我猜,你误解了标签的作用。GCDAsyncSocket是(顾名思义)一个异步套接字。该标签可帮助您将接收到的数据与接收顺序进行匹配,并将发送成功与发送顺序进行匹配。

例如,如果你想发送数据,你使用writeData:messageA withTimeout:-1 tag: tagA(或类似的东西)给你的套接字命令在不久的将来某时发送。现在不一定。并且您可以立即下一个命令以发送另一条消息,例如messageB使用 tag tagB。要知道,messageA确实已发送,您会通过 收到通知socket:aSocket didWriteDataWithTag:aTag。在这里,发送aTagtagAifmessageA的值,以及发送了tagBif的值messageB。标签随消息一起发送;它只帮助您识别您的订单。

在接收方也是如此。您下订单接收(有时)一些数据并为该订单分配一个标签。收到数据后,通知(通过)会显示标签,让您知道哪个订单成功socket:didReadData:withTag:

您可以将标签用于一些语义信息并将其放入您的消息中。但即便如此,通知中的标签是接收订单的标签,而不是发送订单的标签。如果您想在接收方使用您放入消息中的标签,您必须首先接收(至少部分)消息并解析它。

来到您问题的核心:您基本上有两种可能性知道,哪种数据正在到达:

  1. 知道发送数据的顺序并以相同的顺序接收它。
  2. 使用标识数据类型的消息头。仅接收头部并根据头部数据接收和解析消息的剩余部分。

编辑

这是第二种方法的示例。假设您可以发送多个 A、B 类等对象。您的标头可能包括数据的类型和大小:

      typedef struct {
            NSUInteger type_id;
            NSUInteger size;
      } header_t;

      #define typeIdA   1
      #define typeIdB   2
      // ...

一旦你想obj用 objKey 发送一个对象:

     NSMutableData *data = [NSMutableData data];
     NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
     [archiver encodeObject:obj forKey: objKey];

     header_t head;
     if ([obj class] == [A class]) {
        head.type_id = typeIdA;
     } else if ([obj class] == [B class]) {
        head.type_id = typeIdB;
     } else ...

     // ....

     header.size = data.lengh;
     NSData* headData = [NSData dataWithBytes: &header length: sizeof(header)];

     dataWithBytes:length:
     header = NSData.length;
     [asyncSocket writeData:headData withTimeout:-1 tag:headTag];
     [asyncSocket writeData:data withTimeout:-1 tag:dataTag];

如果需要,您可以收到有关成功发送或错误的通知,但我在这里跳过。在接收方,您首先需要一个标头:

    [receiveSocket readDataToLength:sizeof(header_t) withTimeout:-1 tag:rcvHdrTag];
    // rcvHdrTag must not match one of the typeIdX tags

在你的socket:didReadData:withTag:你必须区分,如果你得到标题或遗体(接收遗体是从这里开始的!)

   - (void)socket:(GCDAsyncSocket *)aSocket didReadData:(NSData *)data withTag:(long)tag {
        header_t head;
        id obj;
        id key;

       switch (tag) {
           case rcvHdrTag:
               [data getBytes:&head length:sizeof(header)];
               // now you know what to receive
               [aSocket readDataToLength:header.size withTimeout:-1 tag:header.type];
               return;
               break; // I know, redundancy :-)
          case typeIdA:
               objKey = objKeyA;   // whatever it is...
               break;
          case typeIdB:
               objKey = objKeyB;   
               // ....
       }
       NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
       obj = [unarchiver decodeObjectForKey:objKey];

       // store your object ...
   }

这不是最优雅的例子,它忽略了档案中的对象树和对象间的依赖关系,但你应该明白这一点。

于 2012-08-07T06:19:26.750 回答