2016-03-13 324 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的消息,并发送一个空的消息。