BIO, NIO и AIO — это три разных механизма, используемых в языке программирования Java для обработки операций ввода и вывода (IO). Они представляют собой синхронный блокирующий ввод-вывод, синхронный неблокирующий ввод-вывод и асинхронный неблокирующий ввод-вывод соответственно.
BIO(Blocking IO) Это наиболее традиционная модель ввода-вывода, также известная как ввод-вывод с синхронной блокировкой. Он реализует модель синхронной блокировки, то есть режим реализации сервера — одно соединение и один поток, то есть, когда у клиента есть запрос на соединение, серверу необходимо запустить поток для обработки. Если это соединение ничего не делает, будут вызваны ненужные издержки потока, и поток будет заблокирован во время операции ввода-вывода и не сможет выполнять другие задачи. В среде с высоким уровнем параллелизма производительность BIO низкая, поскольку необходимо создавать поток для каждого соединения, а переключение потоков обходится дорого, но его можно улучшить с помощью механизма пула потоков. BIO подходит для некоторых простых, низкочастотных сценариев связи с коротким соединением, таких как HTTP-запросы.
БИО-модель
преимущество:
недостаток:
Код сервера:
import java.io.*;
import java.net.*;
public class BIOServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
Socket clientSocket = null;
try {
//Создаем Сервер
serverSocket = new ServerSocket(8888);
System.out.println("Сервер запущен, ждём клиентсоединять...");
while (true){
// Слушайте запросы клиентов. Если запрос не получен, он будет ждать вечно.
clientSocket = serverSocket.accept();
int port = clientSocket.getPort();
InetAddress inetAddress = clientSocket.getInetAddress();
System.out.println("клиент "+inetAddress+":"+port+" соединятьуспех!"); //Обработка сообщения клиента
new Thread(new ServerThread(clientSocket)).start();
}
} catch (IOException e) {
System.out.println("клиентсоединятьнеудача:" + e.getMessage());
} finally {
try {
if (clientSocket != null) {
clientSocket.close();
}
if (serverSocket != null) {
serverSocket.close();
}
} catch (IOException e) {
System.out.println("Не удалось закрыть ресурс:" + e.getMessage());
}
}
}
}
/**
* Класс обработки потоков Сервера
*/
class ServerThread implements Runnable{
private Socket clientSocket;
public ServerThread(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
//Получаем входной поток клиента для получения данных клиента
try {
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
//Получаем поток вывода клиента для отправки данных клиенту
PrintWriter out = new PrintWriter(clientSocket.getOutputStream());
int port = clientSocket.getPort();
InetAddress inetAddress = clientSocket.getInetAddress();
String address = inetAddress+":"+port;
String inputLine;
while ((inputLine = in.readLine()) != null) {
//Получаем сообщение клиента
System.out.println("клиент"+адрес+"Сообщение отправлено:" + inputLine);
//Отправляем сообщение клиенту
out.println("Сервер получил сообщение и ответил: "+inputLine);
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Код клиента:
public class BIOClient {
public static void main(String[] args) throws IOException {
Socket clientSocket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
//Привязываем номер порта HTTPipи
clientSocket = new Socket("localhost", 8888);
System.out.println("соединять Серверуспех!");
//Получаем входной поток и получаем сообщение Сервера
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
//Получаем выходной поток и отправляем сообщение на Сервер
out = new PrintWriter(clientSocket.getOutputStream(), true);
Scanner scanner = new Scanner(System.in);
while (true){
System.out.print("Отправить сообщение Серверу:");
String msg = scanner.nextLine();
out.println(msg);
String response;
if ((response = in.readLine()) != null) {
//Получаем ответ Сервера
System.out.println("Ответ сервера:" + response);
}
}
} catch (IOException e) {
System.out.println("соединять Сервернеудача:" + e.getMessage());
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
if (clientSocket != null) {
clientSocket.close();
}
} catch (IOException e) {
System.out.println("Не удалось закрыть ресурс:" + e.getMessage());
}
}
}
}
Результаты запуска:
Сервер
Клиент 1
Клиент 2
Приведенный выше код просто демонстрирует, как использовать БИО-модельодин Серверперениматьи обрабатывать несколькоклиент Статус запроса。Создано здесь3категории,соответственно Сервер BIOServer、многопоточностьклиенткласс обработкиServerThreadи клиентBIOClient,Затем запустите их отдельно СерверBIOServerидваклиентBIOClient,И получите строку ввода с клавиатуры в клиенте и отправьте ее на Сервер.,наконец Сервер Пучокперенимать Полученные данные затем возвращаются вклиент。
Благодаря характеристикам БИО,Значит в Сервере необходимо создавать поток для каждого подключения,Его также можно оптимизировать с помощью пула потоков.,Это всего лишь простая демонстрация без особого дизайна.
NIO — это новая модель ввода-вывода, представленная в Java 1.4, также известная как синхронный неблокирующий ввод-вывод. Она обеспечивает управляемый событиями способ обработки операций ввода-вывода.
По сравнению с традиционной БИО-моделью,NIO использует такие компоненты, как Channel, Buffer и Selector.,Потоки могут отслеживать события ввода-вывода,и продолжить выполнение других задач,Не нужно блокировать и ждать. Когда событие ввода-вывода готово,Тема будет уведомлена,Затем вы можете выполнить соответствующие операции,Реализует масштабируемую сетевую связь в стиле неблокирования. В модели НИО,Данные всегда считываются из канала в буфер.,Или напишите из буфера в канал,Этот режим повышает эффективность ввода-вывода.,И может полностью использовать системные ресурсы.
NIOВ основном состоит из трех частей:Селектор、БуфериКанал。Channelдаодин Может читать и записывать данныеизобъект,Все данные проходятBufferсправиться,Этот подход позволяет избежать записи байтов непосредственно в канал.,Вместо этого данные записываются в буфер, содержащий один или несколько байтов. В многопоточном режиме,Один поток может обрабатывать несколько запросов,Это делается путем регистрации запроса клиента на соединение на мультиплексоре.,Затем мультиплексор опрашивает обработку при поступлении запроса ввода-вывода от соединения.
Для НИО,Если посмотреть на характеристики,Это неблокирующий стиль IO,NдаNon-Blockingзначение;Если с технической точки зрения,NIO — новая технология для БИО,NзначениедаNewзначение。такNIOтакже часто называютNon-Blocking I/OилиNew I/O。
NIO подходит для архитектур с большим количеством соединений и относительно короткими соединениями (легкие операции), таких как чат-серверы, системы заграждения, межсерверные коммуникации и т. д. Это улучшает масштабируемость и производительность одновременной работы системы за счет введения концепции неблокирующих каналов. В то же время использование NIO также упрощает написание программ и повышает эффективность разработки.
Модель НИО
преимущество:
недостаток:
NIO подходит для некоторых сложных, высокочастотных сценариев связи с длительным соединением, таких как чаты, онлайн-игры и т. д.
Прежде чем смотреть код, сначала поймите 3 очень важных компонента NIO.,Селектор、Буфер и Канал:
get()
иput()
ждатьметод来读写буферсерединаизданные。Процесс работы НИО выглядит следующим образом:
select()
методждать Подождите, пока событие произойдет。Следующие два фрагмента кода показывают процесс работы и использование NIO.
Код сервера:
public class NIOServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
// Создайте ServerSocketChannel и привяжите его к указанному порту.
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(9999));
// Установите режим неблокирования
serverSocketChannel.configureBlocking(false);
// Зарегистрируйте ServerSocketChannel в Selector и прослушайте событие OP_ACCEPT
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Сервер запущен, ожидает клиентсоединять...");
while (true) {
// Блокировать, ожидая возникновения события
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) { // Обработка события запроса на соединение
SocketChannel client = serverSocketChannel.accept();
client.configureBlocking(false);
//Прослушиваем событие OP_ACCEPT
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
client.getRemoteAddress();
//Выделяем емкость буфера
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer);
String output = new String(buffer.array()).trim();
Socket socket = client.socket();
InetAddress inetAddress = socket.getInetAddress();
int port = socket.getPort();
String clientInfo = inetAddress+":"+port;
String message = String.format("отклиент %s , Сообщение: %s", clientInfo , output);
System.out.println(message);
System.out.print("Ответное сообщение: ");
writeMessage(selector, client, buffer);
}
keyIterator.remove();
}
}
}
private static void writeMessage(Selector selector, SocketChannel client, ByteBuffer buffer) throws IOException {
Scanner scanner = new Scanner(System.in);
String message = scanner.nextLine();
buffer.clear();
buffer.put(message.getBytes());
//переключаемся из режима записи в режим чтения
buffer.flip();
while (buffer.hasRemaining()) {
client.write(buffer);
}
// Прослушайте еще раз событие OP_ACCEPT.
client.register(selector, SelectionKey.OP_READ);
}
}
Код клиента:
/**
* @author Публичный аккаунт: Цензура(suncodernote)
*/
public class NIOClient {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("localhost", 9999));
socketChannel.register(selector, SelectionKey.OP_CONNECT);
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isConnectable()) {
SocketChannel client = (SocketChannel) key.channel();
if (client.isConnectionPending()) {
client.finishConnect();
}
System.out.print("Enter message to server: ");
Scanner scanner = new Scanner(System.in);
String message = scanner.nextLine();
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
client.write(buffer);
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer);
String output = new String(buffer.array()).trim();
System.out.println("Сообщение от клиента: " + output);
System.out.print("Входное сообщение: ");
// и Сервер Код тот же
writeMessage(selector, client, buffer);
}
keyIterator.remove();
}
}
}
}
Результаты запуска:
серверная часть
сокол
Цангланг
Приведенный выше код создает два новыхкатегории:Сервер(NIOServer
)иклиент(NIOClient
),Вы можете узнать это с помощью приведенного выше кода и результатов выполнения.,При общении в Сервериклиенте,Мы не создавали новый класс потока для связи,Это также одно из самых больших различий между NIO и BIO.
Следует отметить, что,Хотя NIO повышает производительность параллелизма и масштабируемость системы,,Но это также приводит к увеличению сложности программирования и более сложным проблемам отладки. поэтому,При использовании Java NIO,Необходимо тщательно продумать применимые сценарии и модель программирования.
Java AIO (асинхронный ввод-вывод) — это модель асинхронного неблокирующего ввода-вывода, предоставляемая Java. Она поддерживается, начиная с версии Java 7, также называется NIO 2.0.
По сравнению с моделью Модель НИО, AIO дополнительно реализует асинхронный неблокирующий ввод-вывод, улучшая производительность параллелизма и масштабируемость системы. В модели НИОсередина,Хотя несколько запросов на соединение могут обрабатываться через мультиплексор,Но все равно нужно операции чтения и записи на каждом соединении,Определенное количество блокировок все еще существует. И в модели AIO,Все операции ввода-вывода асинхронны.,Не будет блокировать ни одну тему,Системные ресурсы можно использовать более эффективно.
Модель AIO имеет следующие характеристики:
преимущество:
недостаток:
AIO подходит для некоторых сценариев связи с экстремальными, сверхвысокими и сверхдлинными соединениями, таких как облачные вычисления, большие данные и т. д.
Следует отметить, что,В настоящее время модель AIO широко не используется.,Сетевые платформы, такие как Netty, по-прежнему основаны на модели НИО.
Сервер:
/**
* @author Публичный аккаунт: Цензура(suncodernote)
*/
public class AIOServer {
public static void main(String[] args) throws Exception {
// Создает новый канал сокета асинхронного сервера, привязанный к указанному порту.
final AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));
System.out.println("Сервер запускается, ожидает клиентсоединять.");
// Начните принимать новых клиентовсоединять
serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel clientChannel, Void att) {
// Когда новое соединение будет завершено, снова примите новое клиентсоединять.
serverChannel.accept(null, this);
// Создайте новый буфер для чтения данных из
ByteBuffer buffer = ByteBuffer.allocate(1024);
try {
InetSocketAddress clientAddress = (InetSocketAddress) clientChannel.getRemoteAddress();
InetAddress clientIP = clientAddress.getAddress();
int clientPort = clientAddress.getPort();
System.out.println("клиент "+ clientIP + ":" + clientPort + " соединятьуспех。"); } catch (IOException e) {
e.printStackTrace();
}
// Чтение данных из асинхронного канала сокета
clientChannel.read(buffer, buffer, new ReadCompletionHandler(clientChannel));
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("Failed to accept a connection");
}
});
// Держите сервер открытым
Thread.sleep(Integer.MAX_VALUE);
}
}
Чтение обработчика:
/**
* @author Публичный аккаунт: Цензура(suncodernote)
*/
public class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {
private AsynchronousSocketChannel channel;
public ReadCompletionHandler(AsynchronousSocketChannel channel) {
this.channel = channel;
}
@Override
public void completed(Integer result, ByteBuffer attachment) {
// Когда чтение завершится, переверните буфер и распечатайте его.
attachment.flip();
byte[] bytes = new byte[attachment.remaining()];
attachment.get(bytes);
System.out.println("Получено сообщение: " + new String(bytes , StandardCharsets.UTF_8));
attachment.clear();
// Чтение ввода с клавиатуры
Scanner scanner = new Scanner(System.in);
System.out.print("Входное сообщение: ");
String message = scanner.nextLine();
System.out.println();
// Запись данных в асинхронный канал сокета
channel.write(ByteBuffer.wrap(message.getBytes()));
channel.read(attachment , attachment , new ReadCompletionHandler(channel));
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("Failed to read message");
}
}
Клиент:
/**
* @author Публичный аккаунт: Цензура(suncodernote)
*/
public class AIOClient {
public static void main(String[] args) throws Exception {
// Создайте новый канал асинхронного сокета.
AsynchronousSocketChannel clientChannel = AsynchronousSocketChannel.open();
// подключаться к серверу
clientChannel.connect(new InetSocketAddress("localhost", 5000), null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result,Void attachment) {
System.out.println("соединятьприезжать Серверуспех。"); }
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("Failed to connect server");
}
});
// Чтение ввода с клавиатуры
Scanner scanner = new Scanner(System.in);
System.out.print("Отправить сообщение: ");
String message = scanner.nextLine();
// Запись данных в асинхронный канал сокета
clientChannel.write(ByteBuffer.wrap(message.getBytes()), null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
clientChannel.read(buffer, buffer, new ReadCompletionHandler(clientChannel));
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("Failed to write message");
}
});
// Не отключать клиента
Thread.sleep(Integer.MAX_VALUE);
}
}
Результаты испытаний:
Серверный интерфейс:
Клиент 1:
Клиент 2:
Клиент 3:
выше Пример кодасередина,Связь через один Сервер(AIOServer) и 3 клиента(AIOClient),Простая демонстрация использования AIO. можно найти,Методы использования AIO и NIO в основном одинаковы.,Данные считываются из канала в буфер.,Или напишите из буфера в каналсередина,Разница в том, что AIO реализует асинхронный неблокирующий.
BIO, NIO и AIO в Java обрабатывают операции ввода-вывода (I/O), но они различаются методами обработки и эффективностью.
Короче говоря, BIO и NIO и AIO имеют свои особенности. и недостатки, применимые сценарии также различны. BIO подходит для архитектур, где количество подключений небольшое и фиксированное. NIO подходит для сценариев, где количество подключений велико, но одновременных операций чтения и записи относительно мало. AIO подходит для сценариев, где количество подключений велико. и существует множество одновременных операций чтения и записи. При выборе того, какой ввод-вывод использовать, вам необходимо взвесить его с учетом конкретных сценариев и требований приложения. - END -