损坏的协议缓冲消息

时间:2016-03-13 19:25:20

标签: java swift protocol-buffers gcdasyncsocket

我正在使用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)

在两端,我发送一个4字节的Big-Endian整数,表示要遵循的字节数,然后是原始protobuf消息。在两端,我收到要遵循的字节数,阻塞直到我得到那么多字节,然后尝试解析。

在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;
            }
        }
    }

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

调试协议缓冲区消息:

  1. 在Wireshark

  2. 中捕获数据包
  3. 右键单击仅包含protobuf消息的数据包子部分并复制十六进制流

  4. 使用十六进制编辑器将十六进制流保存到文件

  5. protoc ‒‒decode_raw < file并将输出标记和数据与.proto文件中的标记相匹配

  6. 由于异常消息Protocol message contained an invalid tag (zero),我怀疑Swift无法构建protobuf消息并发送了一条空消息。

答案 1 :(得分:1)

知道了!

对socket.writeData的两次调用连续调用不一定是原子的,而是从多个线程调用的。他们正在交错,所以首先它写了一个长度,然后写了不同的长度(和/或其他人的消息)。

在DISPLATCH_QUEUE_SERIAL的dispatch_async块中围绕这两个调用修复了问题。