Следующая система обеспечивает углубленное введение в механизм функции Java Nio и использование по принципам, процессам и т. Д., Давайте изучим его.
Предисловие
В этой статье в основном объясняется механизм ввода IO в Java
Разделен на две части:
Первая часть объясняет механизм IO при многопользовании. Вторая часть объясняет, как оптимизировать отходы ресурсов ЦП в рамках механизма IO (новый IO)
Echo Server
Мне не нужно вводить механизм розетки под одним потоком. Если вы не знаете, вы можете проверить информацию под таким количеством потоков. Что если вы используете гнезда?
Мы используем самый простой сервер Echo, чтобы помочь всем понять
Во-первых, давайте посмотрим на диаграмму рабочих процессов сервера и клиента под многопоточным:
Вы можете видеть, что несколько клиентов отправляют запросы на сервер одновременно
Сервер сделал меру, чтобы включить несколько потоков соответствовать соответствующему клиенту.
И каждый поток завершает свои запросы клиента в одиночку
После того, как принцип закончен, давайте посмотрим, как он реализован
Здесь я написал простой сервер
Используйте технологию пула потоков для создания потоков (я прокомментировал конкретную функцию кода):
открытый класс myserver {private Static executorservice executorservice = executors.newcachedthreadpool (); // Создать пул потоков Private Static Class HandLemsg реализует runnable {// Как только появится новый запрос клиента, создайте этот поток для обработки клиента сокета; // Создать клиент Public HandLemsg (Cocket Client) {// Создание параметра Привязывание this.client = client; } @Override public void run () {BufferedReader BufferedReader = null; // Создать кэш символ ввода потока PrintWriter PrintWriter = null; // Создание символов написания потока try {bufferedReader = new BufferedReader (новый inputStreamReader (client.getInputStream ())); // Получить входной поток клиента printWriter = new PrintWriter (client.getOutputStream (), true); // Получить выходной поток клиента, true - обновить string inputline = null; long a = system.currenttimemillis (); while ((inputline = bufferedreader.readline ())! = null) {printwriter.println (inputline); } long b = System.currentTimeMillis (); System.out.println («Этот поток взял:«+(ba)+"секунды!"); } catch (ioException e) {e.printstackTrace (); } наконец {try {BufferedReader.Close (); printwriter.close (); client.close (); } catch (ioException e) {e.printstackTrace (); }}}} public static void main (string [] args) бросает ioException {// Основной поток сервера используется для прослушивания запросов клиента в цикле ServerSocket Server = New Serversocket (8686); // Создать сервер с портом 8686 Socket Client = null; while (true) {// lop listen client = server.accept (); // Сервер слушает клиентскую систему запросов. executorservice.submit (new Handlemsg (клиент)); // Поместите запрос клиента в поток Handlmsg через пул потоков для обработки}}}В приведенном выше коде мы используем класс, чтобы написать простой сервер Echo, чтобы включить прослушивание порта в основном потоке с помощью мертвого цикла.
Простой клиент
С помощью сервера мы можем получить к нему доступ и отправить несколько строковых данных. Функция сервера - вернуть эти строки и распечатать поток, занимая время.
Давайте напишем простого клиента, чтобы ответить на сервер:
открытый класс myClient {public static void main (string [] args) бросает ioException {socket client = null; PrintWriter PrintWriter = null; BufferedReader BufferedReader = null; try {client = new Socket (); client.connect (new InetsocketAddress ("localhost", 8686)); printwriter = new PrintWriter (client.getOutputStream (), true); printwriter.println ("Привет"); printwriter.flush (); BufferedReader = new BufferedReader (новый inputStreamReader (client.getInputStream ())); // Читать информацию, возвращаемую Server и Soutptor System.out.println («Информация с сервера:»+BufferedReader.readline ()); } catch (ioException e) {e.printstackTrace (); } наконец {printWriter.close (); BufferedReader.Close (); client.close (); }}}В коде мы используем поток символов для отправки строки Hello. Если код в порядке, сервер вернет данные Hello и распечатает информацию журнала, которую мы установили.
Эхо -сервер дисплей результатов
Давай бежим:
1. Откройте сервер и включите прослушивание цикла:
2. Откройте клиент:
Вы можете видеть, что клиент печатает результат возврата
3. Проверьте журнал сервера:
Очень хорошо, реализовано простое многопоточное программирование
Но подумайте об этом:
Если клиент запрашивает, добавьте сон во время записи IO на сервер,
Сделайте каждый запрос занимать поток сервера в течение 10 секунд
Тогда есть много запросов клиентов, каждый из которых занимает так долго
Тогда возможности объединения сервера будут значительно сокращены
Это не потому, что на сервере много тяжелых задач, а только потому, что поток обслуживания ждет ввода -вывода (потому что принять, читать и писать - все блокируют)
Очень неоплачивается высокоскоростные процессор
Что мне делать в это время?
Nio
Новый IO успешно решил вышеуказанную проблему. Как это решило это?
Минимальный блок для IO для обработки запросов клиентов - это потока
Nio использует блок, который на один уровень меньше потока: канал (канал)
Можно сказать, что в NIO требуется только один поток для завершения всего приема, чтения, письма и других операций.
Чтобы узнать NIO, вы должны сначала понять его три основных момента
Селектор, селектор
Буфер, буфер
Канал, канал
Блогер не талантлив, поэтому он нарисовал уродливую картину, чтобы углубить свое впечатление. ^
Дайте мне еще одну диаграмму рабочего процесса NIO под TCP (очень трудно рисовать линии ...)
Просто поймите это примерно, давай шаг за шагом
Буфер
Прежде всего, вам нужно знать, что такое буфер
Взаимодействие данных больше не использует потоки, такие как механизмы ввода -вывода
Вместо этого используйте буфер (буфер)
Блогер считает, что картинки легче всего понять
так...
Вы можете увидеть, где буфер во всем рабочем процессе
Давайте посмотрим на настоящие. Конкретный код на приведенном выше рисунке заключается в следующем:
1. Сначала распределите пространство в буфер, в байтах
Bytebuffer bytebuffer = bytebuffer.allocate (1024);
Создайте объект Bytebuffer и укажите размер памяти
2. Записать данные в буфер:
1). Данные от канала в буфер: channel.read (bytebuffer); 2). Данные от клиента в буфер: bytebuffer.put (...);
3. Прочитайте данные из буфера:
1). Данные от буфера до канала: Channel.write (bytebuffer); 2). Данные от буфера до сервера: bytebuffer.get (...);
Селектор
Селектор является ядром NIO, он является администратором канала
Выполнив метод блокировки select (), прослушайте, готов ли канал
После того, как данные читаются, возвращающимся значением этого метода является количество SelectionKeys
Таким образом, сервер обычно выполняет метод select () Dead Dead Lop, пока не будет готов, а затем не начнет работать
Каждый канал будет связывать событие с селектором, а затем генерировать объект SelectionKey.
Что следует отметить:
Когда канал и селектор связаны, канал должен находиться в неблокирующем режиме.
FileChannel не может переключаться в режим не блокировки, потому что он не является каналом сокета, поэтому FileChannel не может связывать события с селектором
В NIO есть четыре вида событий:
1.selectionKey.op_connect: Событие подключения
2. SelectionKey.op_Accept: Получить события
3. SelectionKey.op_read: Читать события
4. SelectionKey.op_write: написать события
Канал
Всего есть четыре канала:
FileChannel: действует на потоке файлов iO
DatagramChannel: он работает по протоколу UDP
Socketchannel: он работает на протоколе TCP
Serversocketchannel: он действует на протокол TCP
Эта статья объясняет NIO через широко используемый протокол TCP
Давайте возьмем Serversocketchannel в качестве примера:
Откройте канал Serversocketchannel
ServerSocketchannel Serversocketchannel = serversocketchannel.open ();
Закройте канал Serversocketchannel:
serversocketchannel.close ();
Петля носканала:
while (true) {socketchannel socketchannel = serversocketchannel.accept (); ClientChannel.configureBlocking (false);} clientChannel.configureBlocking(false); Заявление состоит в том, чтобы установить этот канал на неблокирование, то есть асинхронное свободное контроль блокировки или неблокировки является одной из характеристик NIO
SelectionKey
SelectionKey является основным компонентом взаимодействия между каналами и селекторами
Например, свяжите селектор на носканале и зарегистрируйте его как событие подключения:
Socketchannel clientChannel = socketchannel.open (); clientChannel.configureBlocking (false); clientChannel.connect (new InetSocketAddress (port)); clientChannel.register (selector, selectionkey.op_connect);
Ядро находится в методе Register (), который возвращает объект SelectionKey
Для обнаружения событий канала - это то, какое событие вы можете использовать следующий метод:
selectionkey.isaceceptable (); selectionkey.isconnectable (); selectionKey.isReadable (); selectionkey.iswritemble ();
Сервер выполняет соответствующие операции при опросе с помощью этих методов
Конечно, ключи, связанные с каналом и селектором, также могут быть получены по очереди.
Канал канал = selectionKey.Channel (); селектор SELECTER = SELECTIONKEY.SELECTOR ();
При регистрации событий на канале мы также можем связать буфер:
clientChannel.register (key.selector (), selectionkey.op_read, bytebuffer.allocatedirect (1024));
Или свяжите объект:
selectionkey.attach (object); объект anthorobj = selectionkey.attachment ();
TCP -сервер NIO
После того, как мы поговорим так много, мы рассмотрим самый простой и самый основной код (не элегантно добавлять так много комментариев, но для всех удобно понимать):
пакет cn.blog.test.niotest; импорт java.io.ioexception; import java.net.inetsocketAddress; импорт java.nio.bytebuffer; импорт java.nio.channels.*; импорт java.nio.charset.charset; import java.Util.iterator; import java.Util.Setecter Selectorer Selectorer Selectorer yemeriecter yemeriector. // Создать селекторный частный окончательный статический int port = 8686; Частный окончательный статический int buf_size = 10240; private void initserver () бросает ioException {// Создать селектор объектов менеджера канала this.selector = selector.open (); // Создать канал канала канала Servercocketchannel Channel = serversocketchannel.open (); Channel.configureblocking (false); // Установить канал в не блокирующий канал. // Привязан канал на порту 8686 // Свяжите вышеуказанный диспетчер каналов и канал, и зарегистрируйте событие OP_ACCET для канала // После регистрации события, SELECTER.Elect () вернется (ключ). Если событие не достигает selector.select (), оно будет блокировать SelectionKey SelectionKey = Channel.register (selector, selectionKey.op_accep); while (true) {// sellector.select (); // Это метод блокировки, подождите, пока данные не будут читаемыми, а возвращаемое значение - это количество ключей (может быть несколько) SET KEYS = SELECTER.SELECTEDKEYS (); // Если канал имеет данные, доступ к сгенерированным клавишам в итератор сбора клавиш iterator = keys.iterator (); // Получить итератор этой коллекции Keys, в то время как (iterator.hasnext ()) {// Используйте итератор, чтобы пройти через коллекцию key = (selectionkey) iterator.next (); // Получить ключевой экземпляр в коллекции iterator.remove (); // Не забудьте удалить этот элемент в итераторе после получения текущего экземпляра ключа, что очень важно, в противном случае возникнет ошибка, если (key.isaceceptable ()) {// Судите, находится ли канал, представляемый текущим ключом в приемлемое состояние, и если это так, DoAccept (Key); } else if (key.isReadable ()) {doread (key); } else if (key.iswrite () && key.isvalid ()) {dowrite (key); } else if (key.isconnectable ()) {system.out.println ("confectable!"); }}}} public void doAccept (клавиша SelectionKey) Throws ioException {sercroverSocketchannel serverChannel = (sercroverocketchannel) key.channel (); System.out.println ("Serversocketchannel слушает в цикле"); Socketchannel clientChannel = serverChannel.accept (); clientChannel.configureblocking (false); clientChannel.register (key.selector (), selectionkey.op_read); } public void doread (клавиша SelectionKey) Throws ioException {socketchannel clientChannel = (socketchannel) key.channel (); Bytebuffer bytebuffer = bytebuffer.allocate (buf_size); long bytesread = clientChannel.read (bytebuffer); while (bytesread> 0) {bytebuffer.flip (); byte [] data = bytebuffer.array (); String info = new String (data) .trim (); System.out.println («Сообщение, отправленное от клиента:»+info); bytebuffer.clear (); bytesread = clientChannel.read (bytebuffer); } if (bytesread ==-1) {clientChannel.close (); }} public void dowrite (key selectionKey) бросает ioException {bytebuffer bytebuffer = bytebuffer.allocate (buf_size); bytebuffer.flip (); Socketchannel clientChannel = (socketchannel) key.channel (); while (bytebuffer.hasreming ()) {clientChannel.write (bytebuffer); } bytebuffer.compact (); } public static void main (string [] args) бросает ioException {mynioServer mynioServer = new mynioServer (); mynoserver.intserver (); }} Я напечатал канал монитора и сказал вам, когда Serversocketchannel начал работать
Если вы сотрудничаете с отладкой клиента NIO, вы можете ясно найти его. Перед входом в опрос select ()
Хотя уже есть ключ для события Accept, Select () не будет вызван по умолчанию
Вместо этого вы должны подождать, пока другие интересные события не будут захвачены Select () перед вызовом Accept SelectionKey
Только тогда начал выполнять круговой мониторинг Serversocketchann
Другими словами, в селекторе всегда поддерживается Serversocketchenel.
И serverChannel.accept(); это действительно асинхронно (канал. Конфигурирование блокировки (false); в методе Initserver)
Если подключение не будет принято, будет возвращено ноль
Если ночечный канал успешно подключен, этот носовой канал регистрирует событие записи (чтение)
И установить асинхронно
Клиент TCP NIO
Должен быть клиент, если есть сервер
На самом деле, если вы можете полностью понять сервер
Код клиента похож
Пакет cn.blog.test.niotest; импорт java.io.ioexception; import java.net.inetsocketAddress; импорт java.nio.bytebuffer; импорт java.nio.channels.selectionkey; импорт java.nio.channels.selector; import java.nio.channels.socketchannel; Mynioclient {частный селекторный селектор; // Создать селекторный частный окончательный статический int port = 8686; Частный окончательный статический int buf_size = 10240; Частный статический байтбаффер bytebuffer = bytebuffer.allocate (buf_size); private void initClient () бросает ioException {this.selector = selector.open (); Socketchannel clientChannel = socketchannel.open (); clientChannel.configureblocking (false); ClientChannel.connect (новый INESetCocketAddress (порт)); clientChannel.register (selector, selectionKey.op_connect); while (true) {selector.select (); Итератор <selectionKey> iterator = selector.selectedKeys (). Iterator (); while (iterator.hasnext ()) {selectionKey key = iterator.next (); iterator.remove (); if (key.isconnectable ()) {doconnect (key); } else if (key.isReadable ()) {doread (key); }}} public void doconnect (клавиша SelectionKey) бросает ioException {socketchannel clientChannel = (socketchannel) key.channel (); if (clientChannel.isconnectionPending ()) {clientChannel.finishConnect (); } clientChannel.configureblocking (false); String info = "Привет сервер !!!"; bytebuffer.clear (); bytebuffer.put (info.getbytes ("utf-8")); bytebuffer.flip (); clientChannel.write (bytebuffer); //clientChannel.register(key.selector(),,selectionKey.op_read); clientChannel.close (); } public void doread (клавиша SelectionKey) Throws ioException {socketchannel clientChannel = (socketchannel) key.channel (); clientChannel.read (bytebuffer); byte [] data = bytebuffer.array (); String msg = new String (data) .trim (); System.out.println ("Сервер отправляет сообщение:"+msg); clientChannel.close (); key.selector (). close (); } public static void main (string [] args) бросает ioException {mynioclient mynioclient = new mynioclient (); mynioclient.initclient (); }}Результат вывода
Здесь я открываю сервер и двух клиентов:
Затем вы можете попытаться открыть тысячу клиентов одновременно. Пока ваш процессор достаточно мощный, сервер не сможет снизить производительность из -за блокировки.
Выше приведено подробное объяснение Java Nio. Если у вас есть какие -либо вопросы, вы можете обсудить их в области сообщений ниже.