как преодолеть "\ n" при использовании метода .readlines () - PullRequest
0 голосов
/ 17 октября 2019

Я использую сокет и получаю ответ от сервера, который является строкой .

Метод readline() продолжает цикл, потому что мой ответне имеет /n или /r в конце каждого ответа. Таким образом, моя программа застревает на этой линии.

Как получить ответ, другими словами, как сообщить методу readline(), что передача окончена, без /n или /r в концеответа? я не могу использовать метод read (), потому что он возвращает int. вот код приема

// Get the return message from the server
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);


// lecture de message

String message = br.readLine();

, и это тот ответ, который я должен получить "1621430458759654852147523101090131125000199000080808078123456123456789125000074125896000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Ответы [ 3 ]

1 голос
/ 17 октября 2019

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

Есливходные данные не содержат разделителей, таких как \n (или какой-либо другой символ или последовательность байтов) между сообщениями, тогда должен быть какой-то другой способ определения количества байтов для чтения. Есть несколько подходов к этому, в зависимости от протокола. Например, в HTTP ответы обычно имеют заголовок Content-Length, чтобы читатель знал, когда заканчивается этот ответ и начинается следующий.

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

Другой подходиспользовать сообщения фиксированного размера и просто читать фиксированное количество байтов каждый раз. Третий подход заключается в использовании разделителя, такого как \n или некоторой другой последовательности байтов, которая не отображается в полезной нагрузке вашего протокола.

Если вам известно количество байтов, которые вы хотите прочитать, сначала создайтебуфер для записи сообщения. Скажем, мы хотим прочитать ровно 500 байтов. Выделите буфер сообщений:

byte messageBuffer[] = new byte[500];

Теперь нам нужно читать из сокета, пока не будет выполнено одно из двух условий:

  • В буфере сообщений 500 байтов
  • Или сокет закрыт

Удобно, что каждый раз, когда мы вызываем read на InputStream сокета, мы получаем количество прочитанных нами байтов, или -1если поток закончилсяСледовательно, мы можем читать в наш буфер сообщений до тех пор, пока мы не заполним его 500 байтами или не получим -1 из вызова read().

В результате мы получим цикл, подобный следующему:

int bytesToRead = 500;
InputStream in = socket.getInputStream();
byte messageBuffer[] = new byte[bytesToRead];
for (int readOffset = 0, readBytes = 0; (readBytes = in.read(messageBuffer, readOffset, messageBuffer.length - readOffset)) != -1
        && readOffset < bytesToRead;) {
    readOffset += readBytes;
}

Или, если хотите, вот так:

int readBytes = 0;
int readOffset = 0;
while (true) {
    readBytes = in.read(messageBuffer, readOffset, messageBuffer.length - readOffset);
    if (readBytes == -1) {
        break;
    }
    readOffset += readBytes;
}

Примечание. Я не проверял этот код.

Как только вы прочитали в буфер достаточно байтов,если вы хотите сделать из них String, просто используйте new String(messageBuffer) или что-то вроде new String(messageBuffer, Charset.forName("UTF-8")), если вы хотите указать кодировку не по умолчанию.

0 голосов
/ 17 октября 2019

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

Что вы делаете?

Это зависит от того, что фактическая структура сообщения. Например:

  • Если каждое сообщение всегда состоит из ровно N символов:

    StringBuilder sb = new StringBuilder(N);
    for (int i = 0; i < N; i++) {
        int ch = br.read();
        if (ch == -1) { 
             throw new IOException("truncated message");
        }
        sb.append((char) ch));
    }
    return sb.toString();
    
  • Если каждое сообщение начинается с размера:

    // read the size
    // read 'size' characters.
    
  • Если каждое сообщение заканчивается символом C (или концом потока)

    StringBuilder sb = new StringBuilder();
    int ch;
    while((ch = br.read()) != C) {
       sb.append((char) ch));
    }
    return sb.toString();
    

И т. Д.

0 голосов
/ 17 октября 2019

Я предполагаю, что вы видите, что эта проблема делает что-то вроде

String hostName = args[0];
int portNumber = Integer.parseInt(args[1]);

try (
    Socket socket = new Socket(hostName, portNumber);
    BufferedReader in =
        new BufferedReader(
            new InputStreamReader(socket.getInputStream()));
    String line = in.readLine();
)
...

Согласно Javadoc для BufferedReader#readLine

Строка считаетсязавершается любым из перевода строки ('\ n'), возврата каретки ('\ r'), возврата каретки, за которым сразу следует перевод строки, или по достижению конца файла (EOF).

Так что эта проблема ожидается. Я предлагаю начать использовать символы окончания строки. Если вы не можете этого сделать, вы можете использовать метод BufferedReader#read и анализировать полученные данные вручную в соответствии с вашими правилами.

...