Socket vs SocketChannel
Я пытаюсь понять SocketChannels и NIO в целом. Я знаю, как работать с обычными сокетами и как создать простой сервер для каждого клиента (используя обычные блокирующие сокеты).
Итак, мои вопросы:
- Что такое SocketChannel?
- Что я могу получить при работе с SocketChannel вместо Socket.
- Какова связь между каналом и буфером?
- Что такое селектор?
- Первое сообщение в документации -
A selectable channel for stream-oriented connecting sockets.
. Что это значит?
Я прочитал также эту документацию, но почему-то я не получаю ее...
Ответы
Ответ 1
A Socket
является блокирующим устройством ввода/вывода. Это делает Thread
, который использует его для блокировки чтения и потенциально также блокирует запись, если базовый буфер заполнен. Поэтому вам нужно создать кучу разных потоков, если на вашем сервере есть куча открытых Socket
s.
A SocketChannel
- это неблокирующий способ чтения из сокетов, так что вы можете обмениваться одним потоком с несколькими открытыми соединениями одновременно. Это работает, добавив связку SocketChannel
в Selector
, затем перейдя по методу селектора select()
, который может уведомить вас, если сокеты приняты, получены данные или закрыты. Это позволяет вам общаться с несколькими клиентами в одном потоке и не иметь накладных расходов нескольких потоков и синхронизации.
Buffer
- еще одна особенность NIO, которая позволяет вам получать доступ к базовым данным из чтения и записи, чтобы избежать накладных расходов на копирование данных в новые массивы.
Ответ 2
К настоящему времени NIO
настолько стар, что немногие помнят, что такое Java до 1.4, что вам нужно знать, чтобы понять "почему" NIO
.
В двух словах, вплоть до Java 1.3, все операции ввода-вывода имели тип блокировки. И хуже того, не было аналога системного вызова select()
для мультиплексного ввода-вывода. В результате сервер, реализованный на Java, не имел другого выбора, кроме как использовать стратегию обслуживания "один поток за соединение".
Основная точка NIO, представленная в Java 1.4, заключалась в том, чтобы реализовать функциональность традиционного мультиплексированного неблокирующего ввода-вывода типа UNIX, доступного на Java. Если вы понимаете, как программировать с помощью select()
или poll()
для обнаружения готовности ввода/вывода в наборе дескрипторов файлов (обычно это сокеты), то вы найдете необходимые вам службы в NIO
: вы будете использовать SocketChannel
для неблокирующих конечных точек ввода-вывода и Selector
для fdsets или массивов pollfd. Теперь становятся возможными серверы с файловыми путями или с потоками, обрабатывающими более одного соединения. Это "лишний".
A Buffer
- это тип массива байтов, который вам нужен для неблокирующего сокета ввода-вывода, особенно на стороне выхода/записи. Если сразу можно записать только часть буфера, с блокировкой ввода-вывода, ваш поток будет просто блокироваться, пока не будет записано целое. С неблокирующим вводом-выводом ваш поток получает возвращаемое значение того, сколько было написано, оставив его вам, чтобы обработать левый конец для следующего раунда. A Buffer
заботится о таких механических деталях, явно применяя шаблон производителя/потребителя для заполнения и слива, при этом понимается, что ваши потоки и ядро JVM не будут синхронизироваться.
Ответ 3
Даже если вы используете SocketChannels
, необходимо использовать пул потоков для обработки channels
.
Размышляя о сценарии, вы используете только один поток, который отвечает за опрос select()
и обрабатывает SocketChannels
, выбранный из Selectors
, если один канал занимает 1 секунду для обработки, а 10 очереди в очереди, это означает, что вам нужно подождать 10 секунд до следующего опроса, который является невыносимым. поэтому для обработки каналов должен быть пул потоков.
В этом смысле я не вижу огромной разницы в структуре шаблонов блокировки потоков на клиенте. основное отличие заключается в шаблоне NIO
, задача меньше, она больше похожа на нить за задачу, а задачи могут быть прочитаны, писать, бизнес-процесс и т.д. для более подробной информации, вы можете взглянуть на Netty реализация NioServerSocketChannelFactory
, которая использует одно соединение, принимающее поток Boss, и отправляет задачи в пул рабочих потоков для обработки
Если вы действительно любите один поток, нижняя строка - это, по крайней мере, вы объединили потоки ввода-вывода, потому что операции ввода-вывода чаще всего медленнее, чем циклы обработки команд, вы не хотели бы, чтобы драгоценный один поток блокируется вводом-выводом, и это точно NodeJS, используя одно соединение для приема потока, и все операции ввода-вывода являются асинхронными и параллельно обрабатываются пулом потоков ввода-вывода
является ли старый стиль потоком за клик мертвым?
Я так не думаю, что программирование NIO является сложным, и многопоточность не является естественным злом. Имейте в виду, что современные операционные системы и процессор становятся лучше и лучше при многозадачности, поэтому накладные расходы многопоточности со временем становятся меньше.