使用GCDASyncSocket时如何分离数据包

时间:2012-08-06 18:06:05

标签: objective-c ios tcp gcdasyncsocket

任何人都可以帮助我吗?我通过使用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];

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

1 个答案:

答案 0 :(得分:8)

我想,你误解了标签的作用。 GCDAsyncSocket(顾名思义)是 asynchrone 套接字。标签可帮助您将接收的数据与接收订单匹配,并将发送成功与发送订单匹配,分别为

例如,如果您想发送数据,请使用writeData:messageA withTimeout:-1 tag: tagA(或类似内容)为您的套接字提供在不久的将来发送某些内容的订单。现在不一定是这样。您可以立即为下一个订单发送另一封邮件,例如messageB,标记为tagB。 要知道,messageA确实已发送,您会通过socket:aSocket didWriteDataWithTag:aTag收到通知。在此处,如果aTag已发送,则tagA的值为messageA,如果tagB已发送,则messageB的值为socket:didReadData:withTag:。标签随邮件一起发送;它只能帮助您识别您的订单。

接收方也是如此。您给订单接收(某些)某些数据并为该订单分配标签。一旦您 接收数据,通知(通过 typedef struct { NSUInteger type_id; NSUInteger size; } header_t; #define typeIdA 1 #define typeIdB 2 // ... )会向您显示标记,告知您哪个订单成功。

您可以将标记用于某些语义信息并将其放入您的消息中。但即使这样,通知中的标记也是接收订单的标记,但绝不是发送订单的标记。如果要使用放在接收方的消息中的标记,则必须先接收(至少部分)消息并解析它。

谈谈你的问题的核心:你基本上有两种可能性来了解哪种数据到达:

  1. 了解发送数据的顺序并按照相同的顺序接收。
  2. 使用标识数据类型的消息头。仅接收头部并根据头部数据接收和解析消息的剩余部分。
  3. 修改

    以下是第二种方法的示例。假设你可以发送一些A,B等类的对象。你的标题可能包括数据的类型和大小:

    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 ... } 中,如果你得到标题或遗骸(在这里开始接收遗体!)

    {{1}}

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