2016-03-13 6 views
0

Я использую Protocol Buffers for Swift (последний из CocoaPods) и официальный буфер клиента Java-протокола Google (версия 2.6.0) для передачи сообщений между сервером Java (ServerSocket) и приложением Swift iOS (GCDAsyncSocket).Корректные буферы протокола Сообщения

Большинство сообщений (несколько сотен в секунду, я потоковое аудио как плавающие массивы, между прочим), поток просто отлично. Однако иногда сообщение от клиента к серверу не анализируется. Код Java бросает

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

На обоих концах Посылают 4-байтовый тупоконечник целого числа, представляющее число байт, чтобы следовать, то сообщению сырой 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; 
      } 
     } 
    } 

Любые идеи?

+0

Другая мысль: может ли быть проблема с подписью ByteBuffer, прочитанной на Java, и UInt32 в Swift? Правильно ли я это делаю? – closeparen

ответ

0

Есть!

Два вызова последовательных вызовов в socket.writeData не обязательно являются атомарными, но вызываются из нескольких потоков. Они чередовались, так что сначала он написал длину, а затем написал другую длину (и/или чужое сообщение).

Окружающий эти два вызова в блоке dispatch_async для DISPATCH_QUEUE_SERIAL исправил проблему.

1

Для протокола отладки сообщений буфера:

  1. захвата пакета в Wireshark

  2. правой кнопкой мыши на пункте пакета, который содержит только сообщение Protobuf и скопировать шестнадцатеричный поток

  3. используйте шестнадцатеричный редактор для сохранения шестнадцатеричного потока в файл

  4. protoc ‒‒decode_raw < file и сопоставить выходные теги и данные теги в файле .proto

В связи с сообщением исключения, Protocol message contained an invalid tag (zero), я подозреваю, что Swift не удалось построить сообщение Protobuf и отправил пустое сообщение.