Я работаю над базовой минимальной концепцией сервера, передающей данные из одного порта в другой пункт назначения по каналам NIO и сокетам.
Вещи отлично работают на полной скорости, быстрые ссылки и т. Д. Вещи ужасно терпят неудачу и потребляют массу процессора, когда одна сторона быстрее, чем другая. Вещи работают, просто работают очень неэффективно.
Пример:
Iterator keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey) keys.next();
keys.remove();
try {
if (!key.isValid()) continue;
if (key.isReadable()) read(key);
}
catch (Exception e) {
e.printStackTrace();
key.cancel();
}
}
Вызов read происходит каждый раз, когда в сокете есть данные, которые можно прочитать. Но что, если записываемая часть этого сокета не может быть записана, потому что клиентская сторона имеет большую задержку или просто не читает данные быстро? Мы в конечном итоге зациклились с безумной скоростью, ничего не делая, пока не освободится немного более записываемый буфер:
public void read(SelectionKey key) throws IOException {
ByteBuffer b = (ByteBuffer) buffers.get(readable); //prior buffer that may or may not have been fully used from the prior write attempt
int bytes_read = readable.read(b);
b.flip();
if (bytes_read > 0 || b.position() > 0) writeable.write(b);
b.compact();
}
Так, скажем, мы можем читать из сокета с гигабитом, но получатель читает только с нашего записываемого сокета со скоростью 100 килобит ... мы могли бы зацикливаться миллион раз между каждым небольшим фрагментом данных, который мы можем записать клиенту поскольку они просто не потребляют данные в буферах сокетов так быстро, как нам нужно.
Если бы я сделал это с потоком, не было бы никаких проблем, поскольку мы блокировали бы вызов write. Но что делать с NIO, чтобы пропустить уведомления о прочтении?