предисловие
Только лысина может стать сильнее
Оглядываясь назад на фронт:
Первоначально я рассчитывал сначала рассмотреть традиционный режим ввода-вывода и уточнить связанные с ним классы традиционного режима ввода-вывода (поскольку существует множество классов ввода-вывода).
Однако оказалось, что в процессе отделкиУже есть много отличных статей, и я могу не достичь их уровня, если организую это сам. иПредполагается, что традиционный IO будет использоваться всеми, но NIO не обязательно.
Ниже я выложу несколько ментальных карт, которые, на мой взгляд, лучше организованы (адрес источника картинок будет указан ниже, вы можете их прочитать):
Структурная схема классифицируется по режиму работы:
Диаграмма сравнения ввода и вывода потока байтов:
Диаграмма сравнения ввода и вывода потока символов:
Схема структуры классификации по объекту операции:
Первоначальный адрес изображения выше, узнайте автора @小明:
иПрочтите исходный код традиционного ввода-выводаОтличная статья о:
Я полагаю, что все прочитают приведенные выше ссылки + поймутРежим упаковки так прост, традиционный ввод-вывод должен подойти~~
А НИО для меня можно сказать вполнестранностьДа, я столкнулся с этим, когда учился в школе.ноЯ не использовал его все время, поэтому остаюсь в курсе: nio доступен из jdk1.4, который более продвинут, чем традиционный ввод-вывод.
Я считаю, что многие новички, как и я, мало что знают о NIO. И мы сейчас выпустили jdk10, а nio jdk1.4 не знает, что немного неразумно.
Так что мне потребовалось несколько дней, чтобы узнатьОсновные точки знаний NIO, во время которого я прочитал модули nio «Идеи программирования на Java» и «Безумные лекции по Java».но, вы обнаружите, что после прочтения она по-прежнему оченьвентиляторЯ не знаю, для чего нужен NIO, и информация в Интернете хорошо не соответствует точкам знаний в книге.
- Онлайн-информация основана на пяти моделях IO, и пять моделей IO также включают множество концепций:
同步/异步/阻塞/非阻塞/多路复用
,И у разных людей разное понимание. - Есть также связанные с unix
select/epoll/poll/pselect
,fd
Эти ключевые слова, люди, у которых нет соответствующего основания, кажутся библией - Это приводит к убеждению, что nio поначалу недосягаемо.
В процессе поиска информации я также собрал много информации по NIO.Эта статьяПонимание NIO с точки зрения новичка. Его можно рассматривать как итог моего просмотра NIO за последние два дня.
- Я надеюсь, что каждый сможет узнать, что такое NIO после прочтения, каковы основные точки знаний NIO и будут использовать NIO~
Тогда давайте начнем.Если в статье есть ошибки, пожалуйста, потерпите меня и поправьте меня в комментариях~
Отказ от ответственности: в этой статье используется JDK1.8.
1. Обзор NIO
в JDK 1.4java.nio.*包
Новая библиотека ввода/вывода Java была представлена вускорить. На самом деле «старые» пакеты ввода-вывода уже используют NIO.Переработано, чтобы получить выгоду, даже если мы явно не используем программирование NIO..
- Неважно, переводится ли nio как неблокирующий io или new io, все имеет смысл~
В «Идеи программирования на Java» читайте ** «Даже если мы явно не используем программирование NIO, мы можем извлечь из этого пользу»Я был очень обеспокоен этим, поэтому: мыПротестируйте** производительность копирования файлов с помощью NIO и традиционного ввода-вывода:
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class SimpleFileTransferTest {
private long transferFile(File source, File des) throws IOException {
long startTime = System.currentTimeMillis();
if (!des.exists())
des.createNewFile();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(des));
//将数据源读到的内容写入目的地--使用数组
byte[] bytes = new byte[1024 * 1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
private long transferFileWithNIO(File source, File des) throws IOException {
long startTime = System.currentTimeMillis();
if (!des.exists())
des.createNewFile();
RandomAccessFile read = new RandomAccessFile(source, "rw");
RandomAccessFile write = new RandomAccessFile(des, "rw");
FileChannel readChannel = read.getChannel();
FileChannel writeChannel = write.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024);//1M缓冲区
while (readChannel.read(byteBuffer) > 0) {
byteBuffer.flip();
writeChannel.write(byteBuffer);
byteBuffer.clear();
}
writeChannel.close();
readChannel.close();
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
public static void main(String[] args) throws IOException {
SimpleFileTransferTest simpleFileTransferTest = new SimpleFileTransferTest();
File sourse = new File("F:\\电影\\[电影天堂www.dygod.cn]猜火车-cd1.rmvb");
File des = new File("X:\\Users\\ozc\\Desktop\\io.avi");
File nio = new File("X:\\Users\\ozc\\Desktop\\nio.avi");
long time = simpleFileTransferTest.transferFile(sourse, des);
System.out.println(time + ":普通字节流时间");
long timeNio = simpleFileTransferTest.transferFileWithNIO(sourse, nio);
System.out.println(timeNio + ":NIO时间");
}
}
Я протестировал размер файла 13M, 40M, 200M соответственно:
1.1 Зачем использовать NIO
Вы можете увидеть повторно реализованный с помощью NIOТрадиционный ввод-вывод вовсе не является ложным, эффект лучше, чем у NIO под большими файлами (конечно, несколько личных тестов могут быть не очень точными)
- NIO требует определенных затрат на обучение, и его не так легко понять, как традиционный IO.
это значит, что мыМожно ли не использовать/не изучать NIO??
ответотрицательныйДа, операции ввода-вывода частодве сценыБудет использоваться следующее:
- файлIO
- сетевой ввод-вывод
НИОGlamour: Использование ввода-вывода в сети может быть отражено!
- Я расскажу об использовании NIO в сети позже, не беспокойтесь~
2. Быстрый старт НИО
Сначала давайте посмотрим наРазница между IO и NIO:
- Можно просто подумать, что:IO — потоковая обработка, NIO — блочная (буферная) обработка.
- Потоковая система ввода/выводаОбработка данных по одному байту за раз.
- Блочная (буферная) система ввода/выводаОбрабатывать данные порциями.
НИО в основном имеетТри основные части:
- буфер буфер
- Канальный конвейер
- Селектор селектор
2.1буферный буфер и канальный конвейер
В NIO данные обрабатываются не в потоке, а в буферных буферах и канальных каналах.С использованиемдля обработки данных.
Просто понять:
- Канальные трубы сравниваются с железными дорогами, а буферные буферы сравниваются с поездами (грузовозы).
А наш НИОБуферный буфер, в котором хранятся данные, транспортируется через конвейер канала для реализации обработки данных.!
- Помните: канал не имеет дело с данными, он отвечает только за транспортировку данных. Буфер Buffer имеет дело с данными.
- Канал --> Транспорт
- Буфер --> Данные
По сравнению с традиционным IO,поток однонаправленный. Для NIO, с концепцией конвейера канала, нашаЧтение и запись являются двунаправленными(Поезд по железной дороге может идти в Пекин из Гуанчжоу, естественно можно вернуться в Гуанчжоу из Пекина)!
2.1.1 Основные моменты буферного буфера
Давайте посмотрим, что стоит отметить в буфере Buffer.
Buffer — это абстрактный класс для буферов:
где БайтбуферСамый используемый класс реализации(Чтение и запись байтовых данных в каналах).
Что мы склонны делать, когда получаем буфер? Это просто, этоЧтение данных из буфера/запись данных в буфер. Итак, основной метод буфера:
put()
get()
Класс Buffer поддерживает четыре свойства основных переменных, чтобы обеспечитьинформация о массивах, которые он содержит. Они есть:
- Емкость Емкость
- Максимальное количество элементов данных, которые может содержать буфер. Емкость устанавливается при создании буфера и никогда не может быть изменена. (Причина, по которой его нельзя изменить, тоже очень проста, нижний слой — это массив)
- Верхняя граница Предел
- Общее количество данных в буфере, который показывает, сколько данных находится в текущем буфере.
- ПозицияПозиция
-
позиция следующего элемента для чтения или записи. Положение автоматически определяется соответствующим
get( )
иput( )
Обновление функций.
-
позиция следующего элемента для чтения или записи. Положение автоматически определяется соответствующим
- Марк Марк
- Место для памятки.Используется для записи позиции последнего чтения и записи.
2.1.2демонстрация кода буфера
Покажи это первымКак создается буфер и как изменяется значение основной переменной.
public static void main(String[] args) {
// 创建一个缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 看一下初始时4个核心变量的值
System.out.println("初始时-->limit--->"+byteBuffer.limit());
System.out.println("初始时-->position--->"+byteBuffer.position());
System.out.println("初始时-->capacity--->"+byteBuffer.capacity());
System.out.println("初始时-->mark--->" + byteBuffer.mark());
System.out.println("--------------------------------------");
// 添加一些数据到缓冲区中
String s = "Java3y";
byteBuffer.put(s.getBytes());
// 看一下初始时4个核心变量的值
System.out.println("put完之后-->limit--->"+byteBuffer.limit());
System.out.println("put完之后-->position--->"+byteBuffer.position());
System.out.println("put完之后-->capacity--->"+byteBuffer.capacity());
System.out.println("put完之后-->mark--->" + byteBuffer.mark());
}
результат операции:
в настоящее времяЯ хочу получить данные из кеша, как взять? ? NIO дал намflip()
метод. Этот метод можетИзменить позицию position и limit!
Тем не менее приведенный выше код, мыflip()
Через некоторое время посмотрим, что происходит со значениями четырех основных атрибутов:
Очевидно, что:
- предел стал положением положения
- и позиция становится 0
Учащиеся, увидев это, могут подумать об этом: когда вызов завершенfilp()
Время:limit — ограничить, где читать, а position — откуда читать
Обычно мы называемfilp()
для **"Переключиться в режим чтения"**
- Вызывается всякий раз, когда данные должны быть прочитаны из буфера
filp()
«Переключиться в режим чтения».
После перехода в режим чтения мы можем прочитать данные в буфере:
// 创建一个limit()大小的字节数组(因为就只有limit这么多个数据可读)
byte[] bytes = new byte[byteBuffer.limit()];
// 将读取的数据装进我们的字节数组中
byteBuffer.get(bytes);
// 输出数据
System.out.println(new String(bytes, 0, bytes.length));
Затем выведите значение базовой переменной, чтобы увидеть:
После чтения мы также хотим записать данные в буфер, затем используйтеclear()
функция, которая "очищает" буфер:
- Данные на самом деле не стираются, это простозабыватьПотерял
2.1.3 Основные моменты канала FileChannel
канал каналОтвечает только за передачу данных, а не непосредственно за рабочие данные. Операционные данные обрабатываются через буферный буфер!
// 1. 通过本地IO的方式来获取通道
FileInputStream fileInputStream = new FileInputStream("F:\\3yBlog\\JavaEE常用框架\\Elasticsearch就是这么简单.md");
// 得到文件的输入通道
FileChannel inchannel = fileInputStream.getChannel();
// 2. jdk1.7后通过静态方法.open()获取通道
FileChannel.open(Paths.get("F:\\3yBlog\\JavaEE常用框架\\Elasticsearch就是这么简单2.md"), StandardOpenOption.WRITE);
использоватьFileChannel с буферомРеализовать функцию копирования файлов:
использоватьфайл с отображением памятиспособ достижениякопия файлаФункция (управлять буфером напрямую):
пройти между каналамиtransfer()
Реализовать передачу данных (напрямую манипулировать буфером):
2.1.4 Прямые и косвенные буферы
- Косвенные буферынеобходимостьПосле этапа копирования (копирование из пространства ядра в пространство пользователя)
- прямой буферненужныйПосле этапа копирования его также можно понимать как --->файл с отображением памяти, (на картинке выше тоже есть примеры).
Существует два способа использования прямых буферов:
- Прямой буфер выделяется при создании буфера
- Вызывается на FileChannel
map()
метод, который отображает файл непосредственно в память для создания
2.1.5разбрасывать и собирать, набор символов
Я чувствую, что эта точка знаний используется очень мало, но во многих учебниках говорится об этой точке знаний, я также возьму ее и расскажу об этом:
- Scatter read (разброс): разбросать данные в одном канале по нескольким буферам
- Gather write (сбор): запись данных из нескольких буферов вместе в один канал
разброс читать
Совокупная запись
Набор символов (пока формат кодирования совпадает с форматом декодирования, проблем нет)
Три, понимание модели ввода-вывода
IO файла подошел к концу, давайте изучим IO в сети~~~ Чтобы лучше понять NIO,Давайте сначала изучим модель IO~
Согласно классификации модели ввода/вывода сетевого программирования UNIX,В UNIX ее можно свести к пяти моделям ввода-вывода.:
- блокировка ввода/вывода
- Неблокирующий ввод-вывод
- Мультиплексирование ввода/вывода
- Управляемый сигналами ввод-вывод
- Асинхронный ввод-вывод
3.0 Основы, необходимые для изучения модели ввода/вывода
3.0.1 Файловые дескрипторы
Ядро Linux будет использовать все внешние устройстваОтноситесь к нему как к файлу, чтение и запись в файл будетВызов системных команд (api), предоставляемых ядром, который возвращаетfile descriptor
(fd, файловый дескриптор). Чтение и запись сокета также будут иметь соответствующий дескриптор, называемыйsocket fd
(дескриптор файла сокета), дескриптор представляет собой число,указывает на структуру в ядре(путь к файлу, область данных и т. д. некоторые свойства).
- Итак: работа файла под линуксом таковаРеализовано с использованием файловых дескрипторов.
3.0.2 Пространство пользователя и пространство ядра
Чтобы убедиться, что пользовательские процессы не могут напрямую манипулировать ядром,Держите ядро в безопасности, система беспокойства делит виртуальное пространство на две части
- часть пространства ядра.
- часть пользовательского пространства.
3.0.3 Процесс операций ввода/вывода
Посмотрим, как работает IO в системе (мыВозьмите читать в качестве примера)
Можно обнаружить, что: когда приложение вызывает метод чтения, необходимождать---> Найдите данные из пространства ядра, а затем скопируйте данные из пространства ядра в пространство пользователя.
- Это ожидание необходимо!
Далее поясняются только три наиболее часто используемые модели ввода/вывода:
- блокировка ввода/вывода
- Неблокирующий ввод-вывод
- Мультиплексирование ввода/вывода
3.1 Модель блокирующего ввода/вывода
Вызывается в пространстве процесса (пользователя)recvfrom
, его системные вызовы до тех пор, пока пакет не прибудет иКопируется в буфер процесса приложения или возвращается при возникновении ошибки,В течение этого периодавсегда жду.
3.2 Модель неблокирующего ввода/вывода
recvfrom
При переходе с прикладного уровня на ядро, если нет данныхвозвращаться напрямуюОшибка EWOULDBLOCK, как правило, для неблокирующих моделей ввода-вывода.Проведите опрос, чтобы проверить этот статус, чтобы увидеть, есть ли у ядра данные.
3.3 Модель мультиплексирования ввода/вывода
Ранее также было сказано: работа с файлами в LinuxРеализовано с использованием файловых дескрипторов.
Под Linux он реализует модель мультиплексирования ввода-вывода следующим образом:
- перечислить
select/poll/epoll/pselect
Одна функция,Передать несколько файловых дескрипторов, если есть файловый дескрипторготов, возвращайся, в противном случае блокируется до истечения времени ожидания.
Напримерpoll()
Функция выглядит следующим образом:int poll(struct pollfd *fds,nfds_t nfds, int timeout);
вpollfd
Структура определяется следующим образом:
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 实际发生了的事件 */
};
- (1) Когда пользовательский процесс вызывает select, весь процесс будет заблокирован;
- (2) При этом ядро будет «контролировать» все сокеты, отвечающие за выбор;
- (3) Когда данные в любом сокете будут готовы, select вернется;
- (4) В это время пользовательский процесс снова вызывает операцию чтения, чтобы скопировать данные из ядра в пользовательский процесс (пространство).
- Таким образом, характеристики мультиплексирования ввода-вывода:Механизм, с помощью которого процесс может одновременно ожидать несколько файловых дескрипторов., и эти файловые дескрипторыЛюбой из них переходит в состояние готовности к чтению, функция выбора()ты можешь вернуться.
Преимущество select/epoll не в том, что он может быть обработан быстрее для одного соединения, а в том, чтодля обработки большего количества подключений.
3.4 Сводная информация о модели ввода/вывода
Серьезные описания приведены выше, я не знаю, понимаете ли вы это. Вот несколько примеров, обобщающих эти три модели:
Блокировка ввода/вывода:
- Java3y пошел купить Hey Tea со своей девушкой, и после длинной очереди мы наконец-то смогли заказать напитки. Я хочу экологических исследований, спасибо. Но HEYTEA не доступна сразу после заказа, поэтому яЖдал час у двери Hey Tea, прежде чем получить егоЗеленые исследования.
- Подождите час у двери
Неблокирующий ввод-вывод:
- Java3y пошел купить немного со своей девушкой, и после длинной очереди мы наконец смогли заказать напиток. Я хочу чай с молоком боба, спасибо. Но немного нельзя взять сразу после заказа,в то же времяОфициант сказал мне: Вам нужно подождать около получаса. Давайте сначала пойдем по магазинам~ Итак, Java3y и его подруга отправились играть с несколькими домовладельцами, и казалось, что время почти истекло. тогдаСпросите еще раз: Ты спросил меня? Мой номер для отслеживания xxx. Официант сказал Java3y: "Еще не пришло. Текущий номер заказа XXX. Вам нужно немного подождать. Вы можете играть рядом. Спрашивая несколько раз, я наконец получил свой чай с молоком боба.
- Я ходил по магазинам, дрался с помещиками и время от времени спрашивал меня,
Модель мультиплексирования ввода/вывода:
- Java3y пошел в McDonald's, чтобы поесть гамбургеры со своей девушкой, и теперь он может использовать апплет WeChat для заказа еды. Так что я нашел место, чтобы сесть с моей девушкой и заказать еду с небольшой программой. После заказа поиграйте в Доужужу, пообщайтесь или что-то в этом роде.Время от времени я слышу, как радио повторяет ХХХ, пожалуйста, возьмите еду., Так или иначе, мой единый номер еще не пришел, поэтому я буду продолжать играть. ~~Просто подождите, пока не услышите радио, и возьмите еду.. Время пролетело быстро, и в это время пришло: Джава3й, пожалуйста, зайди и забери еду. Так что я могу получить свой мальтийский бургер.
- Слушайте радио, чтобы поесть,Вещание не только для меня. Радио позвонило мне, и я пошел за ним, и все было в порядке.
В-четвертых, используйте NIO для завершения сетевого взаимодействия.
Основы 4.1NIO продолжают объяснять
Вернемся к нашей исходной диаграмме:
НИО называетсяno-blocking io
, на самом деле впонимается на сетевом уровне,заТо же самое блокирует для FileChannel.
Мы только объяснили FileChannel раньше, и есть еще несколько каналов для нашей сетевой связи~
Итак: мыкак правилоИспользование NIO используется в сети, и большинство дискуссий о NIO в Интернете ведутся вНа основе сетевого общенияиз! Сказать, что NIO не блокирует, тоже NIOотражение в сетииз!
Из приведенного выше рисунка видно, что есть еще одинSelector
Селектор - это такая штука. Мы говорили это с самого начала, Ниоосновные элементыимеют:
- Буферный буфер
- канал канал
- Селектор селектор
Мы используем NIO в сети, это часто модель ввода/выводамодель мультиплексирования!
- Селектор селектора можно сравнить с Макдональдсомтранслировать.
- Поток может управлять состоянием нескольких каналов.
4.2 Шаблон блокировки NIO
Для лучшего понимания сначала напишем про NIOВ сети код статуса блокировки, тогда проще понять, как пишется неблокирующий.
- Если он блокирует, селектор Selector отсутствует., просто используйте Channel и Buffer напрямую, и все готово.
Клиент:
public class BlockClient {
public static void main(String[] args) throws IOException {
// 1. 获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 6666));
// 2. 发送一张图片给服务端吧
FileChannel fileChannel = FileChannel.open(Paths.get("X:\\Users\\ozc\\Desktop\\新建文件夹\\1.png"), StandardOpenOption.READ);
// 3.要使用NIO,有了Channel,就必然要有Buffer,Buffer是与数据打交道的呢
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 4.读取本地文件(图片),发送到服务器
while (fileChannel.read(buffer) != -1) {
// 在读之前都要切换成读模式
buffer.flip();
socketChannel.write(buffer);
// 读完切换成写模式,能让管道继续读取文件的数据
buffer.clear();
}
// 5. 关闭流
fileChannel.close();
socketChannel.close();
}
}
Сервер:
public class BlockServer {
public static void main(String[] args) throws IOException {
// 1.获取通道
ServerSocketChannel server = ServerSocketChannel.open();
// 2.得到文件通道,将客户端传递过来的图片写到本地项目下(写模式、没有则创建)
FileChannel outChannel = FileChannel.open(Paths.get("2.png"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
// 3. 绑定链接
server.bind(new InetSocketAddress(6666));
// 4. 获取客户端的连接(阻塞的)
SocketChannel client = server.accept();
// 5. 要使用NIO,有了Channel,就必然要有Buffer,Buffer是与数据打交道的呢
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 6.将客户端传递过来的图片保存在本地中
while (client.read(buffer) != -1) {
// 在读之前都要切换成读模式
buffer.flip();
outChannel.write(buffer);
// 读完切换成写模式,能让管道继续读取文件的数据
buffer.clear();
}
// 7.关闭通道
outChannel.close();
client.close();
server.close();
}
}
В результате изображение, переданное клиентом, может быть сохранено локально:
В этот момент сервер сохраняет изображение и хочет сообщить клиенту, что он получил изображение:
Клиент получает данные, принесенные сервером:
Если только приведенный выше коднетиз! Эта программа будетблокироватьВстаньте!
- потому что серверЯ не знаю, есть ли у клиента данные для отправки.(В отличие от начала, клиент закрывает поток после отправки данных, и сервер может знать, что у клиента нет данных для отправки), что заставляет сервер продолжать читать данные, отправленные клиентом.
- Что приводит к блокировке!
Поэтому, когда клиент заканчивает записывать данные на сервер,Явно сообщить серверу, что данные были отправлены!
4.3NIO неблокирующая форма
Если мы используем неблокирующий режим, то мы не можем явно сказать серверу, что данные отправлены. Давайте посмотрим, как написать:
клиент:
public class NoBlockClient {
public static void main(String[] args) throws IOException {
// 1. 获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 6666));
// 1.1切换成非阻塞模式
socketChannel.configureBlocking(false);
// 2. 发送一张图片给服务端吧
FileChannel fileChannel = FileChannel.open(Paths.get("X:\\Users\\ozc\\Desktop\\新建文件夹\\1.png"), StandardOpenOption.READ);
// 3.要使用NIO,有了Channel,就必然要有Buffer,Buffer是与数据打交道的呢
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 4.读取本地文件(图片),发送到服务器
while (fileChannel.read(buffer) != -1) {
// 在读之前都要切换成读模式
buffer.flip();
socketChannel.write(buffer);
// 读完切换成写模式,能让管道继续读取文件的数据
buffer.clear();
}
// 5. 关闭流
fileChannel.close();
socketChannel.close();
}
}
Сервер:
public class NoBlockServer {
public static void main(String[] args) throws IOException {
// 1.获取通道
ServerSocketChannel server = ServerSocketChannel.open();
// 2.切换成非阻塞模式
server.configureBlocking(false);
// 3. 绑定连接
server.bind(new InetSocketAddress(6666));
// 4. 获取选择器
Selector selector = Selector.open();
// 4.1将通道注册到选择器上,指定接收“监听通道”事件
server.register(selector, SelectionKey.OP_ACCEPT);
// 5. 轮训地获取选择器上已“就绪”的事件--->只要select()>0,说明已就绪
while (selector.select() > 0) {
// 6. 获取当前选择器所有注册的“选择键”(已就绪的监听事件)
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
// 7. 获取已“就绪”的事件,(不同的事件做不同的事)
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
// 接收事件就绪
if (selectionKey.isAcceptable()) {
// 8. 获取客户端的链接
SocketChannel client = server.accept();
// 8.1 切换成非阻塞状态
client.configureBlocking(false);
// 8.2 注册到选择器上-->拿到客户端的连接为了读取通道的数据(监听读就绪事件)
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) { // 读事件就绪
// 9. 获取当前选择器读就绪状态的通道
SocketChannel client = (SocketChannel) selectionKey.channel();
// 9.1读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 9.2得到文件通道,将客户端传递过来的图片写到本地项目下(写模式、没有则创建)
FileChannel outChannel = FileChannel.open(Paths.get("2.png"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
while (client.read(buffer) > 0) {
// 在读之前都要切换成读模式
buffer.flip();
outChannel.write(buffer);
// 读完切换成写模式,能让管道继续读取文件的数据
buffer.clear();
}
}
// 10. 取消选择键(已经处理过的事件,就应该取消掉了)
iterator.remove();
}
}
}
}
Или просто нужно:После того, как сервер сохранит изображение, он сообщает клиенту, что изображение было получено..
На стороне сервера просто запишите некоторые данные обратно на сторону клиента:
Если вы хотите получить данные с сервера на клиенте, вам также необходимо зарегистрироваться в реестре (прослушать событие чтения)!
public class NoBlockClient2 {
public static void main(String[] args) throws IOException {
// 1. 获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 6666));
// 1.1切换成非阻塞模式
socketChannel.configureBlocking(false);
// 1.2获取选择器
Selector selector = Selector.open();
// 1.3将通道注册到选择器中,获取服务端返回的数据
socketChannel.register(selector, SelectionKey.OP_READ);
// 2. 发送一张图片给服务端吧
FileChannel fileChannel = FileChannel.open(Paths.get("X:\\Users\\ozc\\Desktop\\新建文件夹\\1.png"), StandardOpenOption.READ);
// 3.要使用NIO,有了Channel,就必然要有Buffer,Buffer是与数据打交道的呢
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 4.读取本地文件(图片),发送到服务器
while (fileChannel.read(buffer) != -1) {
// 在读之前都要切换成读模式
buffer.flip();
socketChannel.write(buffer);
// 读完切换成写模式,能让管道继续读取文件的数据
buffer.clear();
}
// 5. 轮训地获取选择器上已“就绪”的事件--->只要select()>0,说明已就绪
while (selector.select() > 0) {
// 6. 获取当前选择器所有注册的“选择键”(已就绪的监听事件)
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
// 7. 获取已“就绪”的事件,(不同的事件做不同的事)
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
// 8. 读事件就绪
if (selectionKey.isReadable()) {
// 8.1得到对应的通道
SocketChannel channel = (SocketChannel) selectionKey.channel();
ByteBuffer responseBuffer = ByteBuffer.allocate(1024);
// 9. 知道服务端要返回响应的数据给客户端,客户端在这里接收
int readBytes = channel.read(responseBuffer);
if (readBytes > 0) {
// 切换读模式
responseBuffer.flip();
System.out.println(new String(responseBuffer.array(), 0, readBytes));
}
}
// 10. 取消选择键(已经处理过的事件,就应该取消掉了)
iterator.remove();
}
}
}
}
Результаты теста:
нижеКратко обобщитьВажные моменты при использовании NIO:
- Зарегистрируйте канал сокета в селекторе и прослушивайте интересующие вас события.
- Когда интересующее время будет готово, оно перейдет в наш метод обработки для обработки
- После обработки каждого готового события удалите клавишу выбора (потому что мы закончили)
4.4 Конвейеры и DataGramChannel
Я не буду больше говорить об этом здесь, я расскажу о самом сложном TCP, а UDP очень простой.
UDP:
трубопровод:
V. Резюме
В общем, NIO также является относительно важным знанием, потому что это основа для изучения netty~
Очевидно, что невозможно полностью объяснить NIO в одной статье. Если вы хотите узнать больше о NIO, вы можете продолжить изучение по следующей ссылке~
Использованная литература:
- Ууху. Call.com/question/29…--- Как изучить NIO для Java?
- если eve.com/Java-you-oh-ali…--- Серия руководств по Java NIO
- Woohoo. IBM.com/developer Я…----- Начало работы с NIO
- blog.CSDN.net/Бренд Anxin/art IC…-----Введение в модель сетевого ввода-вывода Linux (графика)
- Wangjingxin.top/2016/10/21/…----- Расскажите о NIO и AIO в Java
- www.yiibai.com/java_nio/----- Учебное пособие по Java NIO
- blog.CSDN.net/корова, чем/искусство…------ Java 8: новый ввод-вывод Java (nio)
- blog.CSDN.net/ Еще 15437…-------JAVA NIO (1. Основные понятия, основные классы)
- блог woo woo woo.cn on.com/children's Fairness/fear/686…-----режим ввода-вывода и мультиплексирование ввода-вывода
- Блог woowowoo.cn на.com/E vs Ward/Afraid/You…----Мастер НИО, программа жизни
- blog.CSDN.net/Бренд Anxin/art IC…----Сводка по программированию сетевого ввода-вывода Java (BIO, NIO, AIO содержат полный пример кода)
- Колонка Calling.com/afraid/24393775?…---Атака новичка в Java
- «Идеи программирования на Java»
- Сумасшедшие конспекты лекций по Java
Если в статье есть какие-либо ошибки, пожалуйста, поправьте меня, и мы сможем общаться друг с другом. Учащиеся, привыкшие читать технические статьи в WeChat и желающие получить больше ресурсов по Java, могутОбратите внимание на публичный аккаунт WeChat: Java3y.
Оглавление Навигация по статьям: