Самый эффективный способ чтения в потоке TCP в Java - PullRequest
1 голос
/ 07 мая 2009

Мне нужно изменить код на стороне клиента, потому что протокол связи не был определен должным образом.

Я предположил, что tcp-сообщение от сервера завершится новой строкой, поэтому я использовал reader.readLine () для чтения моих данных.

Теперь мне сказали, что это не тот случай, и вместо этого первые 4 символа сообщения - это длина сообщения, а затем я должен прочитать остальную часть сообщения.

Какой самый эффективный и разумный способ сделать это?

Моя общая идея была следующей:

  1. Создать массив из 4 символов
  2. Прочитать первые 4 символа
  3. Определите длину сообщения
  4. Создать новый массив длины сообщения
  5. читать в новый массив.

Вот пример кода (читатель - это BufferedReader, созданный в другом месте):

char[] chars = new char[4];
int charCount = reader.read(chars);
String messageLengthString = new String(chars);
int messageLength = Integer.parseInt(messageLengthString);
chars = new char[messageLength];
charCount = reader.read(chars);
if (charCount != messageLength)
{
    // Something went wrong...
}

Я знаю, как это сделать, но нужно ли беспокоиться о том, что буферы символов не заполняются? если да, то как мне справиться с этим?

Ответы [ 3 ]

3 голосов
/ 07 мая 2009

Символы в Java предназначены для текста данных. Вы уверены, что протокол действительно определяет длину сообщения таким образом? Скорее всего, первые четыре байта представляют 32-битную длину.

Если вы говорите с разработчиками на C или C ++, они могут использовать "char" как синоним "byte".

РЕДАКТИРОВАТЬ: Хорошо, основываясь на комментарии:

Я бы создал метод, который брал бы Reader и число и неоднократно вызывал read(), пока он не прочитал правильное количество данных или не выдал исключение. Примерно так:

public static String readFully(Reader reader, int length) throws IOException
{
    char[] buffer = new char[length];
    int totalRead = 0;
    while (totalRead < length)
    {
        int read = reader.read(buffer, totalRead, length-totalRead);
        if (read == -1)
        {
            throw new IOException("Insufficient data");
        }
        totalRead += read;
    }
    return new String(buffer);
}

Тогда ваш код может быть:

String lengthText = readFully(reader, 4);
int length = Integer.parseInt(lengthText);
String data = readFully(reader, length);
// Use data now

Вам следует проверить, что происходит, когда они хотят отправить менее 1000 (или более 9999) символов, хотя ...

1 голос
/ 07 мая 2009

Относительно той части вопроса, где вам нужно прочитать определенное количество символов после того, как вы определили, что это такое, с java.io.Readers встречается следующая идиома:

int lengthToRead = getRequiredReadLength(); // Left as exercise to reader :-)
char[] content = new char[lengthToRead]
int from = 0;
while (lengthToRead > 0)
{
   try
   {
      int nRead = reader.read(context, from, lengthToRead);
      if (nRead == -1)
      {
         // End of stream reached before expected number of characters
         // read so handle this appropriately - probably throw an exception
      }
      lengthToRead -= nRead;
      from += nRead;
   }
   catch (IOException e)
   {
      // Handle exception
   }
}

Поскольку вызов read гарантированно возвращает ненулевой результат (вызов блокируется до тех пор, пока не будет доступно некоторых данных , достигнут конец потока (возвращает -1) или исключение этот цикл while гарантирует, что вы будете читать столько символов, сколько вам нужно, если поток может их предоставить.

В общем, всякий раз, когда от Reader запрашивается более одного символа одновременно, следует помнить, что нет никаких гарантий, что фактически было введено много символов, и возвращаемое значение всегда следует проверять, чтобы увидеть, что произошло. В противном случае вы неизбежно столкнетесь с ошибками в какой-то момент, когда части вашего потока «исчезнут».

0 голосов
/ 07 мая 2009

Э-э ... Разве не char в Java 16 бит, для Юникода? Я не думаю, что вы делаете правильно, используя символы для представления байтов, поступающих из сети. Возможно, вам следует использовать что-то вроде <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/nio/ByteBuffer.html" rel="nofollow noreferrer">ByteBuffer</a> из пакета java.nio.

Если вы знаете максимальный размер одного сообщения, вам не помешает просто создать один буфер, прочитать четыре байта в буфер, разобрать их в int или около того, а затем выполнить новое чтение с этим размер, перезаписывая содержимое буфера.

ОБНОВЛЕНИЕ: Выше предполагалось, что протокол был двоичным, и что использование char было "C-ism". Если протокол на самом деле является текстовым, а начальная длина в 4 символа представляет собой дополненное целое число (в некоторой базе, я предполагаю, что 10?), Например, «0047» или «6212», то, вероятно, лучше использовать другой подход, чтобы не иметь перейти от байтов к символам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...