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"))
, если вы хотите указать кодировку не по умолчанию.