Использование сокета для пользовательского протокола через tcp

Мой протокол выглядит так:
[size: UInt16][Channel: Uint16][Protobuff packet]
Проблема в том, что я понимаю, что tcp не гарантирует, что весь пакет, отправленный пользователем, будет отправлен в один. Поэтому технически может быть ситуация, когда я не получаю сразу всего сообщения или даже что часть этого сообщения отправляется вместе с этим сообщением.

Это было довольно просто, когда я просто предполагал, что сразу получаю все сообщение, потому что я просто прочитал первые 2 байта из потока, сделал буфер с соответствующим размером и назвал read(buffer) однако, если я начну думая о возможности отсечения, я не уверен, как с этим справиться. Я попробовал следующее:

  val input = clientSocket.getInputStream() while (!isStopped) { val bufferedInputStream: BufferedInputStream = BufferedInputStream(input) val dataInputStream: DataInputStream = DataInputStream(bufferedInputStream) var messageSize: Int? = null var channel: Int? = null while (true) { if (messageSize == null) { messageSize = dataInputStream.readUnsignedShort() } if (channel == null) { channel = dataInputStream.readUnsignedShort() } var bytesRead = 0 var didReachEnd = false val buffer = ByteArray(messageSize - 4) while (bytesRead != -1 && bytesRead != messageSize) { val temp = dataInputStream.read(buffer) if (temp == -1) { didReachEnd = true } else { bytesRead += temp } Logger.debug(this::class.java, "bytes read: $bytesRead out of $messageSize") } val packet = ChatChannel.Packet.parseFrom(buffer) print(packet.chatMessage.messageText) } } 

И я пытаюсь подражать расщеплению сообщений, выполняя следующие действия со своего клиентского сокета:

  val chatPacket = ChatChannel.Packet.newBuilder().setChatMessage(chatChannel).build() val sendPacket = Packet(0,chatPacket.toByteArray()) val sendPacketByteArray = sendPacket.toByteArray() val halfPoint = sendPacketByteArray.size/2 val firstSlice = sendPacketByteArray.sliceArray(IntRange(0,halfPoint)) val secondSlice = sendPacketByteArray.sliceArray(IntRange(halfPoint,halfPoint+(halfPoint%2))) s.getOutputStream().write(firstSlice) sleep(200) s.getOutputStream().write(secondSlice) 

однако моя реализация просто висит в следующей строке во второй раз:

 bytesRead = dataInputStream.read(buffer,0,buffer.size) 

У меня такое чувство, что я не понимаю, как работает Sockets, однако всякая реализация, которую я видел, предполагает, что когда пользователь будет отправлен, он закроет соединение, но в моем случае это чат-программа, поэтому пользователь только закрывает соединение, когда было сделано в чате.

То, что я хотел бы достичь:

  • размер чтения
  • канал чтения
  • читать байты до тех пор, пока буфер size будет заполнен
  • синтаксический анализ
  • поставить в известность
  • вернуться к шагу 0

Редактирование: после дальнейших исследований я обнаружил, что библиотека MessageLite для работы с protobuf в Java, похоже, имеет методы, называемые writeDelimitedTo (outputStream) и parseDelimitedFrom (inputStream), поэтому, похоже, большая часть работы была выполнена для меня.

    Две вещи:

     while (bytesRead != -1 && bytesRead != messageSize) { bytesRead = dataInputStream.read(buffer,0,buffer.size) 

    Кажется странным для меня. Вы не увеличиваете байты. То есть, если messageSize равно 100, и вы читаете 90 байтов в первом чтении, во втором чтении вы будете читать 10 байт. Вы не можете просто сказать «bytesRead + = …», так как он может вернуть -1. Поэтому вам нужна другая переменная. Проверьте, является ли возвращаемое значение неотрицательным, а затем добавьте его к байту. Этот byteCounter нужно сравнить с messageSize.

    Кроме того: если все, что вы делаете, это предоставление нескольких каналов для сообщений protobuf Google, не делайте этого так! Создайте сообщение protobuf, содержащее сообщение protobuf и переменную канала. Это должно сделать жизнь намного легче.

    Intereting Posts
    Не удалось найти org.jetbrains.kotlinx: kotlinx-html: 0.6.4 GoogleApiClient: невозможно вручную подключиться и выполнить signOut впоследствии Захват изображений и видео, таких как рассказы Snapchat / Instagram Может ли Kotlin испускать аннотации JSR-305 в файлах классов Как определить, является ли объект унаследованным от определенного класса в Котлине? DBFlow: могу ли я удалить один из них с отключением списка? Как перекрестные ссылки в параметрах TreeWalker не работает с Kotlin / Gradle? Конструктор вложения и объект-компаньон В Kotlin, как вы объявляете класс данных с нулевыми параметрами конструктора? `NoClassDefFoundError: android.databinding.DataBindingUtil` при запуске androidTest для фрагмента с ожиданием данных Android Kotlin Защищенное свойство предоставляет свой закрытый тип ExpiresLayout Как использовать ByteArray.getOrElse Сгладить итератор двумерного массива в Котлине Невозможно использовать локальный обработчик аннотаций в проекте Android kotlin