Этому вопросу 7 лет, но у меня была похожая проблема при создании NIO и OIO-совместимой системы (клиент и сервер могут быть любыми, OIO или NIO).
Это было выходом из-за блокировки InputStreams.
Я нашел способ, который делает возможным, и я хочу опубликовать его, чтобы помочь людям с похожими проблемами.
Чтение байтового массива динамических sice выполняется здесь с помощью DataInputStream , который можно просто обернуть вокруг socketInputStream. Кроме того, я не хочу вводить конкретный протокол взаимодействия (например, сначала посылать размер байтов, которые будут отправлены), потому что я хочу сделать это как можно более ванильным. Во-первых, у меня есть простая утилита класса Buffer, которая выглядит следующим образом:
import java.util.ArrayList;
import java.util.List;
public class Buffer {
private byte[] core;
private int capacity;
public Buffer(int size){
this.capacity = size;
clear();
}
public List<Byte> list() {
final List<Byte> result = new ArrayList<>();
for(byte b : core) {
result.add(b);
}
return result;
}
public void reallocate(int capacity) {
this.capacity = capacity;
}
public void teardown() {
this.core = null;
}
public void clear() {
core = new byte[capacity];
}
public byte[] array() {
return core;
}
}
Этот класс существует только из-за глупого пути, байтовый автобокс <=> в Java работает с этим списком. В этом примере это совсем не нужно, но я не хотел ничего исключать из этого объяснения.
Далее, 2 простых основных метода. В них StringBuilder используется как «обратный вызов». Он будет заполнен прочитанным результатом, а количество прочитанных байтов будет возвращено. Конечно, это может быть сделано иначе.
private int readNext(StringBuilder stringBuilder, Buffer buffer) throws IOException {
// Attempt to read up to the buffers size
int read = in.read(buffer.array());
// If EOF is reached (-1 read)
// we disconnect, because the
// other end disconnected.
if(read == -1) {
disconnect();
return -1;
}
// Add the read byte[] as
// a String to the stringBuilder.
stringBuilder.append(new String(buffer.array()).trim());
buffer.clear();
return read;
}
private Optional<String> readBlocking() throws IOException {
final Buffer buffer = new Buffer(256);
final StringBuilder stringBuilder = new StringBuilder();
// This call blocks. Therefor
// if we continue past this point
// we WILL have some sort of
// result. This might be -1, which
// means, EOF (disconnect.)
if(readNext(stringBuilder, buffer) == -1) {
return Optional.empty();
}
while(in.available() > 0) {
buffer.reallocate(in.available());
if(readNext(stringBuilder, buffer) == -1) {
return Optional.empty();
}
}
buffer.teardown();
return Optional.of(stringBuilder.toString());
}
Первый метод readNext
заполнит буфер с byte[]
из DataInputStream и вернет количество байтов, прочитанных таким образом.
Во втором методе, readBlocking
, я использовал природу блокировки, чтобы не беспокоиться о проблемах потребителя-производителя . Просто readBlocking
будет блокироваться, пока не будет получен новый байтовый массив. Прежде чем мы вызовем этот метод блокировки, мы выделяем размер буфера. Обратите внимание, я вызвал reallocate после первого чтения (внутри цикла while). Это не нужно. Вы можете безопасно удалить эту строку, и код все равно будет работать. Я сделал это из-за уникальности моей проблемы.
2 вещи, которые я не объяснил более подробно:
1. в (DataInputStream и единственный короткий вариант здесь, извините за это)
2. отключить (ваша процедура отключения)
В общем, теперь вы можете использовать его следующим образом:
// The in has to be an attribute, or an parameter to the readBlocking method
DataInputStream in = new DataInputStream(socket.getInputStream());
final Optional<String> rawDataOptional = readBlocking();
rawDataOptional.ifPresent(string -> threadPool.execute(() -> handle(string)));
Это предоставит вам способ чтения байтовых массивов любой формы или формы через сокет (или любую реальность InputStream). Надеюсь, это поможет!