2016-03-13 250 views
0

我正在使用Protocol Buffers for Swift(最新來自CocoaPods)和Google官方Java協議緩衝區客戶端(版本2.6.0)在Java服務器(ServerSocket)和Swift iOS應用程序(GCDAsyncSocket)之間傳遞消息。大多數消息(每秒數百次;我將流式音頻作爲浮點數組等等)流動得很好。然而偶爾,客戶端到服務器的消息不會被解析。 Java代碼拋出一個損壞的協議緩衝區消息

com.google.protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero) 

兩端我發送表示跟隨,則原始的protobuf消息的字節數4個字節的大端整數。在兩端,我收到要跟隨的字節數,阻塞直到我得到那麼多字節,然後嘗試解析。

在Java-> Swift方向上沒有觀察到錯誤,只有Swift-> Java。

絕大多數消息都很好。隨着正在處理的消息數量,問題頻率似乎會增加。

在Java中,每個客戶端都有一個線程正在與之通話,還有一個線程正在監聽它。偵聽器線程將消息從線路中拉出並放入每個客戶端的LinkedBlockingQueues中。通話線程從該客戶端的LinkedBlockingQueue消息中拉取消息,將它們序列化並將它們發送到該客戶端的輸出流。

// Take a messageBuilder, serialize and transmit it 
func transmit(messageBuilder: Message_.Builder) { 
    do { 
     messageBuilder.src = self.networkID; 
     let data = try messageBuilder.build().data() 
     var dataLength = CFSwapInt32HostToBig(UInt32(data.length)) 

     self.socket.writeData(NSData(bytes: &dataLength, length: 4), withTimeout: 1, tag: 0) 
     self.socket.writeData(data, withTimeout: 1, tag: 0) 
    } catch let error as NSError { 
     NSLog("Failed to transmit.") 
     NSLog(error.localizedDescription) 
    } 
} 

Java的接收方:

 public void run() { 
     while (true) { 
      try { 
       byte[] lengthField = new byte[4]; 
       try { 
        ghost.in.readFully(lengthField, 0, 4); 
       } catch (EOFException e) { 
        e.printStackTrace(); 
        ghost.shutdown(); 
        return; 
       } 
       Integer bytesToRead = ByteBuffer.wrap(lengthField).order(ByteOrder.BIG_ENDIAN).getInt(); 
       byte[] wireMessage = new byte[bytesToRead]; 
       in.readFully(wireMessage, 0, bytesToRead); 

       HauntMessaging.Message message = HauntMessaging.Message.parseFrom(wireMessage); 

       // do something with the message 


      } catch (IOException e) { 
       e.printStackTrace(); 
       ghost.shutdown(); 
       return; 
      } 
     } 
    } 

任何想法?

+0

另一個想法:在Java中的ByteBuffer讀取和Swift中的UInt32之間是否會有簽名問題?我做對了嗎? – closeparen

回答

0

Got it!

這兩個調用socket.writeData的連續調用不一定是原子的,而是從多個線程中調用。他們正在交錯,所以首先寫了一個長度,然後寫了一個不同的長度(和/或別人的消息)。

圍繞DISPATCH_QUEUE_SERIAL的dispatch_async塊中的那兩個調用修復了這個問題。

1

要調試協議緩衝區中的消息:

  1. 捕獲在Wireshark的分組

  2. 上只包含protobuf的消息的分組分段右擊並複製六角流

  3. 使用十六進制編輯器將十六進制流保存爲文件

  4. protoc ‒‒decode_raw < file和匹配輸出的標籤和數據標籤在你的.proto文件

由於異常消息,Protocol message contained an invalid tag (zero),我懷疑斯威夫特未能建立protobuf的消息,併發送一個空的消息。