2

誰でも私を助けることができますか?GCDAsyncSocket を使用して、TCP プロトコルを介して 2 つのデバイス間で集中的にデータを交換します。次のようなデータを送信します。

     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。ここでaTagは、送信されたtagA場合messageAの値と、送信されたtagB場合の値がありますmessageB。タグはメッセージとともに送信されません。注文を識別するのに役立つだけです。

受け取る側も全く同じです。(いつか) いくつかのデータを受け取るように命令し、まさにその命令にタグを割り当てます。データを受信すると、( 経由の) 通知で、どの注文が成功socket:didReadData:withTag:たかを知らせるタグが表示されます。

タグをセマンティック情報に使用して、メッセージに入れることができます。ただし、その場合でも、通知のタグはreceive orderのタグですが、 send orderのタグではありません。受信側でメッセージに挿入したタグを使用する場合は、最初にメッセージ (少なくとも一部) を受信して​​解析する必要があります。

問題の核心に迫る: 基本的に、どの種類のデータが到着しているかを知るには、次の 2 つの可能性があります。

  1. 送信されたデータの順序を知り、まったく同じ順序で受信します。
  2. データの種類を識別するメッセージ ヘッドを使用します。ヘッドのみを受信し、ヘッド データに応じてメッセージの残りを受信して​​解析します。

編集

2 番目のアプローチの例を次に示します。クラスA、Bなどの多数のオブジェクトを送信できると仮定します。ヘッダーには、データのタイプとサイズを含めることができます。

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

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

objobjKey を使用してオブジェクトを送信したい場合:

     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 に答える