Как избежать блокировки с помощью Java ServerSocket?
Im работает с прослушивателем сокета, который должен прослушивать 2 порта для двух типов данных (порт 80 и порт 81). Эти данные очень похожи, как в случае операций, выполняемых на данных, и просто отличаются, потому что они приходят n разных портов. Я пошел вперед и закодировал реализацию с использованием класса Java ServerSocket, только чтобы понять позже, что метод accept() класса ServerSocket является блоком, и моя реализация не может этого себе позволить. Так что теперь я думал о том, чтобы реализовать то же самое с помощью Java NIO, но после того, как я прошел через некоторые учебные пособия, я думаю, что я более смущен, чем то, как я начал. Было бы здорово, если бы кто-то здесь мог пройти меня через весь процесс, даже если бы он был в псевдокоде или техническом "что делать дальше".
Это то, что я планирую достичь.
Слушайте, как навсегда на 2 портах, вызывая 2 похожих потока (не блокируя)
Удаленное устройство из какого-либо сетевого местоположения соединяется, отправляет данные и затем отключается.
Я думаю, что если только знание того, как NIO можно использовать для настройки сервера для прослушивания на порту, говорит порт 80, на локальном хосте, все остальное довольно легко реализовать.
Приветствия
Ответы
Ответ 1
NIO вызывается, когда вам нужно масштабировать до нескольких тысяч одновременных подключений.
В противном случае я бы предложил использовать несколько потоков. Для каждого порта (и его соответствующего ServerSocket
) создайте поток, который вызывает accept()
в цикле. Эти вызовы будут блокироваться, но это нормально, потому что другие потоки запущены, заботясь о любых доступных задачах.
Когда новый Socket
принят, создайте еще один поток, посвященный этому соединению. Это зависит от приложения, но обычно этот поток читается из сокета (операция блокировки) и выполняет запрошенную операцию, записывая результаты обратно в сокет.
Эта архитектура будет масштабироваться до многих сотен подключений на большинстве настольных платформ. И модель программирования довольно проста, если каждое соединение самодостаточно и независимо от других (это позволяет избежать проблем concurrency). Введение NIO обеспечит большую масштабируемость, но требует большой сложности.
Ответ 2
Вот небольшой пример, чтобы начать работу с NIO.
Это сервер, который прослушивает порты 80 и 81 и печатает все, что получено на стандартном выходе. Соединение будет закрыто после приема пакета, начинающегося с CLOSE
; весь сервер отключается после получения пакета, начинающегося с QUIT
. Отсутствие части отправки и обработки ошибок может быть немного лучше.: -)
public static void main() throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1024);
Selector selector = Selector.open();
ServerSocketChannel server1 = ServerSocketChannel.open();
server1.configureBlocking(false);
server1.socket().bind(new InetSocketAddress(80));
server1.register(selector, OP_ACCEPT);
ServerSocketChannel server2 = ServerSocketChannel.open();
server2.configureBlocking(false);
server2.socket().bind(new InetSocketAddress(81));
server2.register(selector, OP_ACCEPT);
while (true) {
selector.select();
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
SocketChannel client;
SelectionKey key = iter.next();
iter.remove();
switch (key.readyOps()) {
case OP_ACCEPT:
client = ((ServerSocketChannel) key.channel()).accept();
client.configureBlocking(false);
client.register(selector, OP_READ);
break;
case OP_READ:
client = (SocketChannel) key.channel();
buffer.clear();
if (client.read(buffer) != -1) {
buffer.flip();
String line = new String(buffer.array(), buffer.position(), buffer.remaining());
System.out.println(line);
if (line.startsWith("CLOSE")) {
client.close();
} else if (line.startsWith("QUIT")) {
for (SelectionKey k : selector.keys()) {
k.cancel();
k.channel().close();
}
selector.close();
return;
}
} else {
key.cancel();
}
break;
default:
System.out.println("unhandled " + key.readyOps());
break;
}
}
}
}
Obs: поля SelectionKey
(OP_ACCEPT
...) статически импортируются:
import static java.nio.channels.SelectionKey.*;
Ответ 3
Многие фреймворки, такие как Apache MINA и Netty, были реализованы на основе Java NIO для повышения неблокирующего программирования ввода-вывода. Я настоятельно рекомендую им сделать вашу программу NIO радостью, а не кошмаром. Они соответствуют вашей проблеме.
Кроме того, попробуйте использовать эффективный протокол как в размере транспортного сообщения, так и при кодировании/декодировании (сериализации/десериализации). Буферы протокола Google - это надежное решение в этой области. Также посмотрите Kryo и KryoNet, Они могут быть полезны.