Оригинальная HD-карта разума (xmind/pdf/jpg
) можете подписаться на официальный аккаунт:一枝花算不算浪漫
Отвечатьnio
Вот и все.
предисловие
Прошу прощения, что давно не обновлял оригинальную статью.После прочтения последнего времени обновления, оно затянулось больше чем на месяц.
Я учился все это времяNetty
Соответствующее знание, потому что здесь задействовано много точек знаний, оно также потребовало много обходных путей. В настоящее время чистые учебные материалы в Интернете ослепительны.Постепенный способ разделения общего количества не только для того, чтобы увидеть лес, но и для того, чтобы увидеть растительность.
Раньше я был с другом в Ханчжоу.СяофэйТакже было упомянуто, что первоначальные намерения обоих в этом отношении одинаковы, и я надеюсь, что больше друзей смогут присоединиться к совместному изучению и обсуждению.(PS: эта статья изучена и организована вместе с Xiaofei~)
Netty
представляет собой асинхронную, управляемую событиями структуру веб-приложений и инструмент, основанный наNIO
Среды клиентского и серверного программирования. Итак, мы начинаем сNIO
Проанализируйте и объясните основную основу, связанную с зависимостями, чтобы служитьNetty
Начало познавательного пути.
Обзор основ сетевого программирования
1. Socket
Socket
Он имеет значение самого «сокета», что не является уникальным понятием в Java, а является независимым от языка стандартом.Любой язык программирования, который может реализовать сетевое программирование, имеетSocket
. существуетLinux
В среде специальный тип файлов, используемый для представления сетевого взаимодействия между процессами, его сущность представляет собой псевдофайл, формируемый ядром с помощью буферов. Поскольку это файл, конечно, мы можем использовать файловый дескриптор для ссылки на сокет.
Подобно трубам,Linux
Целью системы инкапсулировать его в файл является унификация интерфейса, чтобы операции чтения и записи сокетов и чтения и записи файлов были согласованы. Разница в том, что каналы в основном используются для локального межпроцессного взаимодействия, а сокеты в основном используются для передачи данных между сетевыми процессами.
Это можно понять так:Socket
Два приложения на этой сетевой связи для обмена данными через интерфейс программирования API двунаправленной связи.
Socket
Конкретные этапы основного потока коммуникации следующие:
(1) Через серверListen
Включите мониторинг и дождитесь доступа клиента.
(2) Сокет клиента проходит черезConnect
Подключиться к сокету на стороне сервера, сторона сервера проходитAccept
Получение клиентских подключений. существуетconnect-accept
Во время процесса операционная система выполнит трехстороннее рукопожатие.
(3) Клиент и сервер черезwrite
а такжеread
Отправляйте и получайте данные, ОС завершитTCP
Подтверждение данных, повторная передача и другие шаги.
(4) пройтиclose
Чтобы закрыть соединение, операционная система помашет четыре раза.
Для языка программирования Java,java.net
Пакеты — это фундаментальные библиотеки классов для сетевого программирования. вServerSocket
а такжеSocket
Это основной тип сетевого программирования.
SeverSocket
Тип серверного приложения.Socket
тип установленного соединения. Когда соединение установлено успешно, и сервер, и клиент будут иметьSocket
Пример объекта, вы можете передать этоSocket
Пример объекта, который завершает все операции сеанса. Для полного подключения к сетиSocket
равны, случай оценки сервер-клиент отсутствует.
2. Введение в модель IO
Для операции ввода-вывода данные будут сначала скопированы в пространство ядра, а затем скопированы из пространства ядра в пространство пользователя, поэтому один разread
Операция будет проходить в два этапа:
(1) Ожидание подготовки данных
(2) Скопируйте данные из пространства ядра в пространство пользователя
На основе двух вышеуказанных этапов генерируются пять различных режимов ввода-вывода.
- Блокировка ввода-вывода: подчиненный процесс инициирует операцию ввода-вывода и ожидает завершения двух вышеуказанных фаз, в это время эти две фазы блокируются вместе.
- Неблокировка IO: процесс продолжает задавать, готов ли IO, а затем инициирует операцию чтения, когда она готова, а затем копирует данные из пространства ядра в пространство пользователя. Первый этап не блокирует, но опросы, второй этап блоки.
- Мультиплексирование ввода-вывода: несколько подключений используют один и тот же выбор, чтобы спросить, готов ли ввод-вывод.Если он готов, он вернет, что данные готовы, а затем соответствующее соединение инициирует операцию чтения для передачи данных из ядра. скопировано в пространство пользователя. Двухступенчатая раздельная блокировка.
- Управляемый сигналом ввод-вывод: когда процесс инициирует операцию чтения, он немедленно возвращается.Когда данные будут готовы, он уведомит процесс в форме уведомления, и процесс инициирует операцию чтения, чтобы скопировать данные из пространство ядра в пространство пользователя. Первый этап не блокирует, второй этап блокирует.
- Асинхронный ввод-вывод: когда процесс инициирует операцию чтения, он немедленно возвращается.После того, как данные будут готовы и скопированы в пользовательское пространство, процесс будет уведомлен о получении данных. Ни одна сцена не блокирует.
Эти пять режимов ввода-вывода нетрудно найти по двум парам отношений: синхронные и асинхронные, блокирующие и неблокирующие. Итак, чтобы немного объяснить:
синхронный и асинхронный
- Синхронизировать:Синхронизация означает, что после совершения вызова вызов не возвращается до тех пор, пока вызываемый абонент не обработает запрос.
- асинхронный:Асинхронный означает, что после того, как вызов инициирован, ответ вызываемого объекта немедленно получен, чтобы указать, что запрос был получен, но вызываемый объект не вернул результат. В это время мы можем обрабатывать другие запросы. Вызываемый объект обычно полагается на события, обратные вызовы и другие механизмы для уведомления. Вызывающий возвращает результат.
Самая большая разница между синхронным и асинхронным режимом заключается в том, что если он асинхронный, вызывающему объекту не нужно ждать результата обработки, и вызываемый объект уведомит вызывающего о своем возвращаемом результате с помощью таких механизмов, как обратные вызовы.
блокирующий и неблокирующий
- блокировать:Блокировка заключается в инициировании запроса, а вызывающий объект ожидает возврата результата запроса, то есть текущий поток будет приостановлен, не сможет заниматься другими задачами и сможет продолжить работу только тогда, когда будут готовы условия.
- Неблокирующий:Неблокирующий — это инициация запроса, вызывающему абоненту не нужно ждать возврата результата, и он может сначала заняться другими делами.
Блокировка и неблокировка — это разные способы доступа процесса к данным в соответствии с состоянием готовности операции ввода-вывода, грубо говоря, это метод реализации метода операции чтения или записи. будет ждать вечно, в неблокирующем режиме метод чтения или записи немедленно вернет значение состояния.
Если объединенные блоки синхронизации (blocking-IO
) короткоBIO
, синхронный неблокирующий (non-blocking-IO
) короткоNIO
и асинхронный неблокирующий (asynchronous-non-blocking-IO
) короткоAIO
Что это значит?
- BIO(Режим синхронного блокирующего ввода-вывода): Чтение и запись данных должны быть заблокированы в потоке, ожидающем его завершения. Здесь используется классический пример с кипящей водой. Предположим, что есть сцена с кипящей водой. Есть ряд чайников с кипящей водой. Режим работы БИО заключается в том, чтобы попросить нить оставаться в чайнике, пока чайник не закипит, прежде чем перейти к следующий чайник. Но на самом деле поток ничего не делает, ожидая, пока закипит чайник.
- NIO(Синхронный неблокирующий): Поддерживаются как блокирующий, так и неблокирующий режимы, но здесь мы проиллюстрируем его синхронным неблокирующим режимом ввода-вывода, так что же такое синхронный неблокирующий режим? Если вы также возьмете кипящую воду в качестве примера, подход NIO состоит в том, чтобы попросить поток постоянно опрашивать состояние каждого чайника, чтобы увидеть, изменилось ли состояние любого чайника, чтобы перейти к следующему шагу.
- AIO(Модель асинхронного неблокирующего ввода-вывода): в чем разница между асинхронным неблокирующим и синхронным неблокирующим? Асинхронная неблокировка не требует, чтобы поток опрашивал изменения состояния всех операций ввода-вывода.После соответствующего изменения состояния система уведомит соответствующий поток для обработки. На каждом чайнике установлен переключатель, соответствующий кипячению воды.После того, как вода закипит, чайник автоматически оповестит меня о том, что вода закипела.
java
серединаBIO
,NIO
а такжеAIO
понял как даJava 语言
На уровне операционной системы эти триIO
Инкапсуляция модели. Когда программисты используют эти инкапсулированные API, им не нужно заботиться о знании операционной системы и писать разные коды для разных операционных систем.Java
API на нем. Поэтому, чтобы дать читателям более конкретное и рекурсивное понимание этих трех моделей иNIO
Есть явный контраст, который продолжается ниже.
Java BIO
BIO
Метод программирования обычно является древним продуктом Java, существовавшим со времен JDK 1.0-JDK1.4. Процесс реализации программирования таков: сначала запуститеServerSocket
Для прослушивания сетевых запросов клиент запускаетSocket
Инициировать сетевой запрос, по умолчаниюSeverSocket
Будет создан поток для обработки запроса, и если на сервере нет ни одного потока, клиент будет заблокирован в ожидании или будет отклонен. Режим реализации сервера — одно соединение и один поток, то есть, когда у клиента есть запрос на соединение, серверу необходимо запустить поток для обработки. Общая структура выглядит следующим образом:
Если вы хотите, чтобыBIO
Коммуникационная модель способна обрабатывать несколько клиентских запросов одновременно, поэтому необходимо использовать многопоточность (главным образом потому, чтоsocket.accept()
,socket.read()
,socket.write()
Три основные задействованные функции синхронно заблокированы), то есть он создает новый поток для каждого клиента для обработки ссылки после получения запроса на подключение клиента. После завершения обработки он возвращает ответ клиенту, поток уничтожен . Это типичная модель связи запрос-ответ. Мы можем представить ненужные накладные расходы на потоки, если это соединение ничего не делает, но это можно сделать с помощьюмеханизм пула потоковУлучшенные пулы потоков также могут сделать создание и повторное использование потоков относительно дешевыми. После использования механизма пула потоков для улучшенияBIO
Схема модели выглядит следующим образом:
BIO
Этот метод подходит для архитектуры с относительно небольшим количеством соединений и фиксированным числом соединений.Этот метод имеет относительно высокие требования к ресурсам сервера, а параллелизм ограничен приложениями.Это единственный выбор перед JDK1.4, но программа интуитивно понятна и проста в понимании.Java BIO
В интернете много примеров программирования, поэтому я не буду приводить здесь примеры кодирования, в конце концов, позжеNIO
В этом-то и дело.
Java NIO
NIO
(Новый ввод-вывод или ввод-вывод без блокировки), представленный начиная с JDK1.4.非阻塞IO
, это非阻塞
+ 同步
режим связи. здесьNo Blocking IO
используется для различения вышеперечисленныхBIO
.
NIO
хочу решитьBIO
проблемы параллелизма, черезReactor模式
событийный механизм для достиженияNon Blocking
из. когдаsocket
Есть потоки, доступные для чтения или записиsocket
Операционная система уведомит приложение о соответствующей обработке, и приложение будет считывать поток в буфер или записывать в операционную систему.
Другими словами, в настоящее время это не соединение, соответствующее потоку обработки, а действительный запрос, соответствующий потоку.Когда в соединении нет данных, нет рабочего потока для его обработки.
Когда соединение создано, ему не нужно соответствовать потоку, соединение будет зарегистрировано в多路复用器
выше, так что все соединения могут быть выполнены только с одним потоком, когда多路复用器
При опросе, если есть запрос на соединение, для обработки открывается только один поток, то есть один запрос на режим потока.
NIO
обеспечивает то же самоеSocket
а такжеServerSocket
СоответствующийSocketChannel
а такжеServerSocketChannel
Две разные реализации канала сокета, как показано в следующей структуре. участие здесьReactor
Шаблоны проектирования, мультиплексированиеSelector
,Buffer
Не беспокойтесь об этом пока, я расскажу об этом позже.
Метод NIO подходит для архитектур с большим количеством соединений и относительно короткими соединениями (легкая операция), таких как чат-серверы, где параллелизм ограничен приложениями, а программирование сложно, JDK1.4 начал его поддерживать. в то же время,NIO
Отличие от обычного IO можно в основном отличить от носителя хранения данных, заблокирован ли он и т.д.:
Java AIO
а такжеNIO
Отличие в том, что при выполнении операций чтения и записи нужно только напрямую вызывать APIread
илиwrite
метод. Оба метода являются асинхронными.Для операций чтения, когда есть поток для чтения, операционная система будет передавать доступный для чтения поток.read
метод и уведомить приложение; для операций записи, когда операционная системаwrite
Операционная система активно уведомляет приложение, когда поток, переданный методом, был записан. То есть его можно понимать какread/write
Все методы являются асинхронными, и функция обратного вызова будет активно вызываться после завершения. существуетJDK7
, предоставляет реализации асинхронных файловых каналов и асинхронных каналов сокетов, которые называютсяNIO
.
AIO
Этот метод используется для архитектур с большим количеством подключений и относительно длинными подключениями (тяжелыми операциями), такими как серверы альбомов, для полного вызоваOS
Участвуйте в параллельных операциях, программирование сложнее,JDK7
Начните поддерживать.
насколько я могу судитьAIO
Применение не очень широкое,Netty
Пробовал это раньшеAIO
, но сдался.
2. Введение основных компонентов NIO
1. Channel
существуетNIO
, Все основные операции ввода-выводаChannel
начал,Channel
пройти черезBuffer(缓冲区)
Выполнять операции чтения и записи.
read()
Указывает на чтение данных в канале в буфер,write()
Указывает, что данные буфера записываются в канал.
Channel
Существует много классов реализации, вот три наиболее часто используемых:
-
SocketChannel
: Канал, по которому клиент инициирует TCP-соединение. -
ServerSocketChannel
: сервер прослушивает TCP-канал нового соединения. Для каждого нового клиентского соединения будет установлен соответствующий SocketChannel. -
FileChannel
: Чтение и запись данных из файла
вSocketChannel
а такжеServerSocketChannel
Он чаще всего используется в сетевом программировании, и конкретное использование будет объяснено в окончательном примере кода через некоторое время.
2. Buffer
концепция
Buffer
Также известный как буфер памяти, который по сути является блоком памяти, мы можем записывать данные в эту память, а затем читать данные из этой памяти. Эта память также может быть инкапсулирована какNIO Buffer
объект и предоставляет набор часто используемых методов, облегчающих нам операции чтения и записи в этом блоке памяти.
Buffer
существуетjava.nio
определяется как абстрактный класс в:
мы можем поставитьBuffer
Это понимается как инкапсуляция массива, наша наиболее часто используемаяByteBuffer
Соответствующая структура данныхbyte[]
Атрибуты
Buffer
Есть 4 очень важных свойства:вместимость, предел, положение, отметка
-
capacity
Атрибут: емкость, максимальное значение элементов данных, которое может содержать буфер, назначается при инициализации и создании буфера и не может быть изменено.
На приведенном выше рисунке емкость инициализированного буфера равна 8 (от 0 до 7 на рисунке, всего 8 элементов), поэтомуcapacity = 8
-
limit
Атрибут: представляет верхний предел чтения и записи буфера.- В режиме записи:
limit
Представляет верхнее предельное положение, в которое можно записывать данные, на этот разlimit = capacity
В режиме чтения: в
Buffer
После записи всех данных вызовомflip()
метод, переключитесь в режим чтения, на этот разlimit
равныйBuffer
Размер фактически записанных данных. потому чтоBuffer
может быть не заполнено, поэтомуlimit<=capacity - В режиме записи:
-
position
Атрибут: представляет чтение или записьBuffer
позиция. По умолчанию 0.- В режиме записи: каждый
Buffer
напишите значение,position
Он автоматически увеличится на 1, представляя позицию следующей записи. - В режиме чтения: каждый
Buffer
считывает значение,position
Он автоматически добавит 1 для представления позиции следующего чтения.
- В режиме записи: каждый
Из приведенного рисунка хорошо видно, что в режиме чтения и записиемкость, предел, положениеотношение.
-
mark
Атрибут: представляет метку.С помощью метода mark() текущее значение позиции записывается, и значение позиции присваивается метке.В последующем процессе записи или чтения текущая позиция может быть восстановлена методом reset() , Значение фиксируется меткой.
С этими важными атрибутами покончено, можно еще раз вспомнить:
0 <= mark <= position <= limit <= capacity
Отношения между этими атрибутами теперь должны быть ясны~
Буферизация общих операций
Создать буфер
allocate(int capacity)
ByteBuffer buffer = ByteBuffer.allocate(1024);
int count = channel.read(buffer);
создан в примереByteBuffer
это объект, основанный на куче памяти.
wrap(array)
wrap
метод может обернуть массив вBuffer
Объект:
ByteBuffer buffer = ByteBuffer.wrap("hello world".getBytes());
channel.write(buffer);
allocateDirect(int capacity)
пройти черезallocateDirect
метод также может быстро создать экземплярBuffer
объект иallocate
очень похоже, разница вот в чемallocateDirect
создан на основепамять вне кучиОбъект.
Память вне кучи не находится в куче JVM и не управляется сборщиком мусора. Когда память вне кучи выполняет некоторые операции ввода-вывода базовой системы, эффективность будет выше.
Операция записи в буфер
Buffer
Писать можно черезput()
а такжеchannel.read(buffer)
Пишите двумя способами.
Обычно, когда мы читаем операции NIO, мы начинаем сChannel
чтение данных запись вBuffer
, что соответствуетBuffer
изоперация записи.
Операция чтения буфера
Buffer
Чтение может осуществляться черезget()
а такжеchannel.write(buffer)
Читать двумя способами.
Опять же, мы правыBuffer
Операция чтения , наоборот, вернаChannel
изоперация записи. читатьBuffer
данные, а затем написатьChannel
середина.
Другие распространенные методы
-
rewind()
: сбросить позицию на 0, и буфер снова может быть прочитан и записан.Как правило, этот метод подходит для операций чтения, которые можно понимать как повторное чтение буфера.
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
-
flip()
: очень распространенный метод, обычно используемый при переключении из режима записи в режим чтения. Он также установит позицию в 0, а затем установит предел, равный исходной записанной позиции.
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
-
clear()
: Сброс данных в буфере, этот способ в основном для режима записи, т.к. установлен лимит емкости, в режиме чтения будут проблемы.
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
-
mark()&reset()
:mark()
Метод заключается в сохранении текущегоposition
к переменнойmark
г, то поreset()
метод восстановления текущегоposition
дляmark
, код реализации очень прост:
public final Buffer mark() {
mark = position;
return this;
}
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
Обычно используемые методы чтения и письма можно резюмировать с помощью рисунка:
3. Selector
концепция
Selector
Мы часто говорим, что это один из самых важных компонентов NIO.多路复用器
означаетSelector
компоненты.Selector
Компонент используется для опроса одного или несколькихNIO Channel
Является ли состояние доступным для чтения и записи. С помощью механизма опроса можно управлять несколькими каналами, что означает возможность управления несколькими сетевыми подключениями.
механизм опроса
- Во-первых, канал должен быть зарегистрирован в селекторе, чтобы селектор знал, какими каналами нужно управлять.
- Затем Селектор будет непрерывно опрашивать прописанный на нем Канал.Если у определенного Канала есть время чтения или записи, Канал будет опрошен Селектором, а затем через SelectionKey можно будет получить готовую коллекцию Каналов для последующих IO-операций.
манипулирование недвижимостью
- Создать селектор
пройти черезopen()
метод, мы можем создатьSelector
объект.
Selector selector = Selector.open();
- Зарегистрировать канал в селекторе
мы должныChannel
зарегистрироваться наSelector
, чтобы иметь возможность бытьSelector
управлять.
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
определенныйChannel
зарегистрироваться вSelector
, то Канал должен бытьнеблокирующийВсе вышеупомянутый код имеетconfigureBlocking()
операция конфигурации.
существуетregister(Selector selector, int interestSet)
Второй параметр метода, идентифицирующийinterest
Коллекции, то есть, какие события интересуют Selector, могут отслеживать четыре различных типа событий:
public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << ;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;
-
Connect事件
: Событие завершения соединения (TCP-соединение), только для клиентов, соответствующее SelectionKey.OP_CONNECT. -
Accept事件
: Принимать новые события подключения, применимые только к серверу, соответствующему SelectionKey.OP_ACCEPT. -
Read事件
: событие чтения, применимое к обоим концам, соответствующее SelectionKey.OP_READ , указывающее, что буфер доступен для чтения. -
Write事件
: время записи, применимое к обоим концам, соответствующее SelectionKey.OP_WRITE , указывающее, что буфер доступен для записи.
Channel
Запускается событие, указывающее, что время готово:
- Клиентский канал успешно подключается к другому серверу и становится «готовым к подключению».
- Сокет сервера готов к приему новых входящих подключений, называемых «готов к приему».
- Канал с доступными для чтения данными, называемый «готовым к чтению».
- Канал, ожидающий записи данных, называется «готов к записи».
Конечно,Selector
Можно одновременно интересоваться несколькими событиями, и мы можем использовать операцию ИЛИ для объединения нескольких событий:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
Селектор некоторых других операций
Выберите канал
public abstract int select() throws IOException;
public abstract int select(long timeout) throws IOException;
public abstract int selectNow() throws IOException;
Когда селектор выполняетсяselect()
Метод заблокируется и немедленно вернется, когда зарегистрированный на нем канал будет готов, возвращая количество готовых.
select(long timeout)
вselect()
Механизм тайм-аута добавлен на основе .selectNow()
Возврат сразу без блокировки.
Очень важно отметить одну вещь: select
метод возвращенint
значение, указывающее, сколькоChannel
готов.
с момента последнего звонкаselect
сколько после методаChannel
Быть готовым. Если называетсяselect
метод, потому что существуетChannel
Когда он становится готовым, он возвращает 1;
Если вы позвоните сноваselect
метод, если другойChannel
Когда все будет готово, он снова вернет 1.
Получите действенный канал
Set selectedKeys = selector.selectedKeys();
Когда будут готовы новыеChannel
,передачаselect()
метод, ключ будет добавлен в коллекцию Set.
3. Пример кода
Впереди так много предзнаменований, в основном, чтобы все понялиNIO
Пример кода также удобен для того, чтобы каждый мог написать его самостоятельно в будущем.NIO
Программа для сетевого программирования. Основные шаги для создания сервера NIO следующие:
1. 打开ServerSocketChannel,监听客户端连接 2. 绑定监听端口,设置连接为非阻塞模式 3. 创建Reactor线程,创建多路复用器并启动线程 4. 将ServerSocketChannel注册到Reactor线程中的Selector上,监听ACCEPT事件 5. Selector轮询准备就绪的key 6. Selector监听到新的客户端接入,处理新的接入请求,完成TCP三次握手,建立物理链路 7. 设置客户端链路为非阻塞模式 8. 将新接入的客户端连接注册到Reactor线程的Selector上,监听读操作,读取客户端发送的网络消息 9. 异步读取客户端消息到缓冲区 10.对Buffer编解码,处理半包消息,将解码成功的消息封装成Task 11.将应答消息编码为Buffer,调用SocketChannel的write将消息异步发送给客户端
NIOServer.java
:
public class NIOServer {
private static Selector selector;
public static void main(String[] args) {
init();
listen();
}
private static void init() {
ServerSocketChannel serverSocketChannel = null;
try {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(9000));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NioServer 启动完成");
} catch (IOException e) {
e.printStackTrace();
}
}
private static void listen() {
while (true) {
try {
selector.select();
Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();
while (keysIterator.hasNext()) {
SelectionKey key = keysIterator.next();
keysIterator.remove();
handleRequest(key);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
private static void handleRequest(SelectionKey key) throws IOException {
SocketChannel channel = null;
try {
if (key.isAcceptable()) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
channel = serverSocketChannel.accept();
channel.configureBlocking(false);
System.out.println("接受新的 Channel");
channel.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int count = channel.read(buffer);
if (count > 0) {
System.out.println("服务端接收请求:" + new String(buffer.array(), 0, count));
channel.register(selector, SelectionKey.OP_WRITE);
}
}
if (key.isWritable()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("收到".getBytes());
buffer.flip();
channel = (SocketChannel) key.channel();
channel.write(buffer);
channel.register(selector, SelectionKey.OP_READ);
}
} catch (Throwable t) {
t.printStackTrace();
if (channel != null) {
channel.close();
}
}
}
}
NIOClient.java
:
public class NIOClient {
public static void main(String[] args) {
new Worker().start();
}
static class Worker extends Thread {
@Override
public void run() {
SocketChannel channel = null;
Selector selector = null;
try {
channel = SocketChannel.open();
channel.configureBlocking(false);
selector = Selector.open();
channel.register(selector, SelectionKey.OP_CONNECT);
channel.connect(new InetSocketAddress(9000));
while (true) {
selector.select();
Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();
while (keysIterator.hasNext()) {
SelectionKey key = keysIterator.next();
keysIterator.remove();
if (key.isConnectable()) {
System.out.println();
channel = (SocketChannel) key.channel();
if (channel.isConnectionPending()) {
channel.finishConnect();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("你好".getBytes());
buffer.flip();
channel.write(buffer);
}
channel.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = channel.read(buffer);
if (len > 0) {
System.out.println("[" + Thread.currentThread().getName()
+ "]收到响应:" + new String(buffer.array(), 0, len));
Thread.sleep(5000);
channel.register(selector, SelectionKey.OP_WRITE);
}
}
if(key.isWritable()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("你好".getBytes());
buffer.flip();
channel = (SocketChannel) key.channel();
channel.write(buffer);
channel.register(selector, SelectionKey.OP_READ);
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally{
if(channel != null){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(selector != null){
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
распечатать результат:
// Server端
NioServer 启动完成
接受新的 Channel
服务端接收请求:你好
服务端接收请求:你好
服务端接收请求:你好
// Client端
[Thread-0]收到响应:收到
[Thread-0]收到响应:收到
[Thread-0]收到响应:收到
4. Резюме
Просмотрите использованиеNIO
Этапы разработки серверной программы:
- Создайте
ServerSocketChannel
и пул потоков бизнес-процессов. - Привяжите порт прослушивания и настройте его в неблокирующем режиме.
- Создайте
Selector
, ранее созданныйServerSocketChannel
зарегистрироваться наSelector
вкл., мониторSelectionKey.OP_ACCEPT
. - Выполнение цикла
Selector.select()`` 方法,轮询就绪的
Канал`. - опрос готов
Channel
, если вOP_ACCEPT
Статус, указывающий, что это новый клиентский доступ, вызывающийServerSocketChannel.accept
Получите новых клиентов. - Настроить новый доступ
SocketChannel
для неблокирующего режима и зарегистрируйтесь вSelector
вкл., мониторOP_READ
. - Если опрос
Channel
статусOP_READ
, указывающий, что есть новые готовые пакеты для чтения, а затем построитьByteBuffer
объект, чтение данных.
Из этих шагов мы в основном знаем, что знания, с которыми должны быть знакомы разработчики:
-
jdk-nio
Предусмотрено несколько ключевых классов:Selector
,SocketChannel
,ServerSocketChannel
,FileChannel
,ByteBuffer
,SelectionKey
- Необходимо знать сетевые знания: распаковка липких пакетов tcp, сетевая флэш-память, переполнение тела пакета и повторная передача и т. д.
- надо знать
linux
Базовая реализация, как ее правильно закрытьchannel
, как выйти из системыselector
, как избежатьselector
слишком часто - нужно знать, как
client
конечная прибыльserver
Возвращаемое значение терминала, а затем возвращаемое на передний план, как ждать или как сделать механизм предохранителя - Необходимо знать сериализацию объектов и алгоритм сериализации
- Пропустите и подождите, потому что мне уже немного неудобно. Как программист, я привык к удобным и простым API, и я могу написать более надежный и безошибочный код, не зная слишком много о базовых деталях...
Недостатки нативного API NIO:
① Компоненты NIO сложны:использовать роднойNIO
Для разработки серверной части и клиентской стороны необходимо задействовать канал сокета сервера (ServerSocketChannel
), канал сокета (SocketChannel
), Селектор (Selector
), буфер (ByteBuffer
) и другие компоненты, принципы и API этих компонентов должны быть знакомы сNIO
разработка и отладка, а затем необходимо отлаживать и оптимизировать приложение
② Фонд развития NIO: NIO
Порог несколько выше, и для разработки и оптимизации разработчикам необходимо освоить многопоточность, сетевое программирование и т. д.NIO
приложение для сетевого общения
③ Базовая обработка передачи собственного модуля связи сети разработки API:Передача по сети не только реализует функцию передачи данных сервера и клиента, но также обрабатывает различные нештатные ситуации, такие как механизм отключения и повторного подключения, обработка перегрузки сети, обработка исключений, обработка залипших пакетов, обработка распаковки, механизм кэширования и т. д. Проблема в том, что это функция, которую должны иметь все зрелые сетевые приложения, иначе можно сказать, что это только демо-версия начального уровня.
④ ОШИБКА NIO: NIO
Есть некоторые ошибки в себе, такие какEpoll
, в результате чего селектор (Selector
) пустой опрос, не разрешенный в JDK 1.7
Netty
существуетNIO
На основе , он инкапсулирует нативную JavaNIO API
, какие из вышеперечисленных проблем были решены?
Сравнил Java Nio, используйтеNetty
Программы развития, упрощение каких шагов это? ... Эта серия вопросов, и поэтому мы также собираемся задавать вопросы. Но только потому, что эта презентацияNIO
Соответствующие знания, без введенияNetty API
использовать, поэтому представитьNetty API
Преимущества использования простой разработки и низкого порога немного несостоятельны. Тогда оставь это позади и открой для всехNetty
Учебное путешествие, чтобы узнать, что все считают хорошимNetty
Так ли хороши слухи о реках и озерах?
С нетерпением ждем последующегоNetty
Отправиться в путешествие!