UDP / Сокеты дейтаграмм
Модуль dgram предоставляет реализацию сокетов UDP-дейтаграмм.
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('error', (err) => {
console.log(`server error:\n${err.stack}`);
server.close();
});
server.on('message', (msg, rinfo) => {
console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
});
server.on('listening', () => {
const address = server.address();
console.log(`server listening ${address.address}:${address.port}`);
});
server.bind(41234);
// server listening 0.0.0.0:41234
Класс: dgram.Socket
Объект dgram.Socket является объектом EventEmitter, который инкапсулирует функциональность дейтаграмм.
Новые экземпляры dgram.Socket создаются с помощью dgram.createSocket(). Ключевое слово new не должно использоваться для создания экземпляров dgram.Socket.
Событие: 'close'
Событие 'close' генерируется после закрытия сокета с помощью close(). После срабатывания новые события 'message' для этого сокета больше не будут генерироваться.
Событие: 'error'
-
exception<Ошибка>
Событие 'error' генерируется всякий раз, когда возникает ошибка. Обработчик событий получает единственный объект Error.
Событие: 'listening'
Событие 'listening' генерируется всякий раз, когда сокет начинает прослушивать сообщения дейтаграмм. Это происходит сразу после создания сокетов UDP.
Событие: 'message'
Событие 'message' генерируется, когда новое сообщение дейтаграммы доступно в сокете. Обработчик событий получает два аргумента: msg и rinfo.
socket.addMembership(multicastAddress[, multicastInterface])
Уведомляет ядро о присоединении к группе multicast по указанному multicastAddress и multicastInterface с помощью опции сокета IP_ADD_MEMBERSHIP. Если аргумент multicastInterface не указан, операционная система выберет один интерфейс и добавит членство к нему. Для добавления членства ко всем доступным интерфейсам вызовите addMembership по одному разу для каждого интерфейса.
При совместном использовании сокета UDP несколькими cluster работниками функция socket.addMembership() должна вызываться только один раз, иначе произойдёт ошибка EADDRINUSE.
const cluster = require('cluster');
const dgram = require('dgram');
if (cluster.isMaster) {
cluster.fork(); // Works ok.
cluster.fork(); // Fails with EADDRINUSE.
} else {
const s = dgram.createSocket('udp4');
s.bind(1234, () => {
s.addMembership('224.0.0.114');
});
}
socket.address()
Возвращает объект, содержащий информацию об адресе сокета. Для сокетов UDP этот объект будет содержать свойства address, family и port.
socket.bind([port][, address][, callback])
-
port<целое число> -
address<строка> -
callback<Функция> без параметров. Вызывается по завершении привязки.
Для сокетов UDP это заставляет сокет dgram.Socket прослушивать сообщения дейтаграмм по указанному port и необязательному address. Если port не указан или равен 0, операционная система попытается привязаться к произвольному порту. Если address не указан, операционная система попытается прослушать все адреса. После завершения привязки генерируется событие 'listening', и необязательная функция callback вызывается.
Обратите внимание, что указание обработчика событий 'listening' и передача callback методу socket.bind() не приведёт к ошибкам, но не очень полезно.
Привязанный сокет дейтаграмм удерживает процесс Node.js работающим для приёма сообщений дейтаграмм.
Если привязка завершится неудачно, генерируется событие 'error'. В редких случаях (например, при попытке привязать закрытый сокет), может быть выброшено исключение Error.
Пример сервера UDP, прослушивающего порт 41234:
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('error', (err) => {
console.log(`server error:\n${err.stack}`);
server.close();
});
server.on('message', (msg, rinfo) => {
console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
});
server.on('listening', () => {
const address = server.address();
console.log(`server listening ${address.address}:${address.port}`);
});
server.bind(41234);
// server listening 0.0.0.0:41234
socket.bind(options[, callback])
-
options<Объект> Обязательно. Поддерживает следующие свойства:-
port<Целое число> -
address<строка> -
exclusive<логическое значение>
-
-
callback<Функция>
Для сокетов UDP это заставляет сокет dgram.Socket прослушивать сообщения дейтаграмм по указанному port и необязательному address, которые передаются как свойства объекта options в качестве первого аргумента. Если port не указан или равен 0, операционная система попытается привязаться к произвольному порту. Если address не указан, операционная система попытается прослушать все адреса. После завершения привязки генерируется событие 'listening', и необязательная функция callback вызывается.
Обратите внимание, что указание обработчика событий 'listening' и передача callback методу socket.bind() не приведёт к ошибкам, но не очень полезно.
Объект options может содержать дополнительное свойство exclusive, которое используется при работе с объектами dgram.Socket с модулем cluster. Если exclusive установлено в false (по умолчанию), рабочие процессы кластера будут использовать один и тот же базовый обработчик сокета, позволяя совместно обрабатывать подключения. Если же exclusive установлено в true, обработчик не используется совместно, и попытка совместного использования порта приводит к ошибке.
Привязанный сокет дейтаграмм удерживает процесс Node.js работающим для приёма сообщений дейтаграмм.
Если привязка завершится неудачно, генерируется событие 'error'. В редких случаях (например, при попытке привязать закрытый сокет), может быть выброшено исключение Error.
Пример сокета, прослушивающего эксклюзивный порт, показан ниже.
socket.bind({
address: 'localhost',
port: 8000,
exclusive: true
});
socket.close([callback])
Закрывает базовый сокет и прекращает прослушивание данных на нём. Если указана функция обратного вызова, она добавляется в качестве обработчика события 'close'.
socket.dropMembership(multicastAddress[, multicastInterface])
Уведомляет ядро о выходе из группы multicast по multicastAddress с использованием опции сокета IP_DROP_MEMBERSHIP Этот метод автоматически вызывается ядром при закрытии сокета или завершении процесса, поэтому большинству приложений не нужно вызывать его.
Если multicastInterface не указан, операционная система попытается удалить членство на всех доступных интерфейсах.
socket.send(msg, [offset, length,] port, address[, callback])
-
msg<Buffer> | <строка> | <массив> Сообщение для отправки. -
offset<число> Целое число. Смещение в буфере, с которого начинается сообщение. -
length<число> Целое число. Количество байтов в сообщении. -
port<число> Целое число. Порт назначения. -
address<строка> Имя хоста или IP-адрес назначения. -
callback<Функция> Вызывается, когда сообщение было отправлено.
Отправляет дейтаграмму по сокету. Необходимо указать адрес port и address.
Аргумент msg содержит сообщение для отправки. В зависимости от его типа, может применяться различное поведение. Если msg является Buffer, offset и length задают смещение в Buffer, с которого начинается сообщение, и количество байтов в сообщении соответственно. Если msg является String, он автоматически преобразуется в Buffer с кодировкой 'utf8'. При сообщениях, содержащих многобайтовые символы, offset и length будут рассчитываться относительно длины в байтах, а не позиции символа. Если msg является массивом, offset и length указывать не нужно.
Аргумент address — строка. Если значение address — имя хоста, для разрешения адреса хоста будет использоваться DNS. Если address не указан или пустой, вместо него будут использоваться '127.0.0.1' или '::1'.
Если сокет не был предварительно привязан с помощью вызова bind, сокету назначается случайный номер порта, и он привязывается к адресу «все интерфейсы» ('0.0.0.0' для udp4 сокетов, '::0' для udp6 сокетов).
В качестве опции может быть задана функция callback, чтобы сообщать об ошибках DNS или определять, когда безопасно повторно использовать объект buf. Обратите внимание, что поиск DNS задерживает отправку минимум на один такт цикла событий Node.js.
Единственный способ убедиться, что дейтаграмма была отправлена, — использовать callback. Если произойдет ошибка и будет передан callback, ошибка будет передана в качестве первого аргумента функции callback. Если callback не задан, ошибка будет отправлена как событие 'error' объекта socket.
Смещение и длина являются необязательными, но если вы указали одно, вам необходимо указать и другое. Кроме того, они поддерживаются только когда первый аргумент — Buffer.
Пример отправки UDP-пакета на случайный порт на localhost;
const dgram = require('dgram');
const message = Buffer.from('Some bytes');
const client = dgram.createSocket('udp4');
client.send(message, 41234, 'localhost', (err) => {
client.close();
});
Пример отправки UDP-пакета, составленного из нескольких буферов, на случайный порт на localhost;
const dgram = require('dgram');
const buf1 = Buffer.from('Some ');
const buf2 = Buffer.from('bytes');
const client = dgram.createSocket('udp4');
client.send([buf1, buf2], 41234, 'localhost', (err) => {
client.close();
});
Отправка нескольких буферов может быть быстрее или медленнее в зависимости от вашего приложения и операционной системы: проверьте это с помощью бенчмарка. Обычно это быстрее.
Примечание о размере UDP-дейтаграммы
Максимальный размер UDP-дейтаграммы зависит от IPv4/v6 (максимальной единицы передачи) и от размера поля MTU.
-
Поле
Payload Lengthимеет ширину16 bits, что означает, что обычная полезная нагрузка превысит 64К байт включая заголовок интернет-протокола и данные (65 507 байт = 65 535 − 8 байт заголовка UDP − 20 байт заголовка IP); это обычно верно для интерфейсов обратной связи, но такие длинные дейтаграммы непрактичны для большинства хостов и сетей. -
MTU— это максимальный размер, который может поддерживать данный технология канального уровня для дейтаграмм. Для любого каналаIPv4требует минимальногоMTUв68байтах, а рекомендуемыйMTUдля IPv4 —576(обычно рекомендуется какMTUдля приложений типа dial-up), независимо от того, приходят они целиком или фрагментами.Для
IPv6, минимальныйMTUсоставляет1280байт, однако, обязательный минимальный размер буфера для сборки фрагментов составляет1500байт. Значение68байт очень мало, поскольку у большинства современных технологий канального уровня, таких как Ethernet, есть минимальнаяMTUв1500.
Невозможно заранее узнать MTU каждого канала, через который может пройти пакет. Отправка дейтаграммы, большего размера, чем MTU получателя, не сработает, потому что пакет будет молча отброшен без уведомления источника о том, что данные не достигли адресата.
socket.setBroadcast(flag)
-
flag<логическое>
Устанавливает или очищает опцию сокета SO_BROADCAST. При установке в true UDP-пакеты могут быть отправлены на адрес широковещательной передачи локального интерфейса.
socket.setMulticastInterface(multicastInterface)
-
multicastInterface<Строка>
Примечание: Все ссылки на область в этом разделе относятся к индексам зоны IPv6, которые определены RFC 4007. В строковом формате IP с индексом области записывается как 'IP%scope', где scope — имя интерфейса или номер интерфейса.
Устанавливает интерфейс исходящей многоадресной рассылки по умолчанию для сокета на выбранный интерфейс или возвращает к выбору интерфейса системой. multicastInterface должен быть допустимой строковой записью IP из семейства сокета.
Для сокетов IPv4 это должен быть IP, настроенный для желаемого физического интерфейса. Все пакеты, отправленные в многоадресную рассылку по сокету, будут отправлены по интерфейсу, определенному последним успешным использованием этого вызова.
Для сокетов IPv6 multicastInterface должен содержать область, чтобы указать интерфейс, как в примерах ниже. В IPv6 отдельные вызовы send также могут использовать явный scope в адресах, поэтому только пакеты, отправленные на адрес многоадресной рассылки без явного указания scope, будут затронуты последним успешным использованием этого вызова.
Примеры: IPv6 исходящий интерфейс многоадресной рассылки
На большинстве систем, где формат scope использует имя интерфейса:
const socket = dgram.createSocket('udp6');
socket.bind(1234, () => {
socket.setMulticastInterface('::%eth1');
});
На Windows, где формат scope использует номер интерфейса:
const socket = dgram.createSocket('udp6');
socket.bind(1234, () => {
socket.setMulticastInterface('::%2');
});
Пример: IPv4 исходящий интерфейс многоадресной рассылки
Все системы используют IP хоста на желаемом физическом интерфейсе:
const socket = dgram.createSocket('udp4');
socket.bind(1234, () => {
socket.setMulticastInterface('10.0.0.2');
});
Результаты вызова
Вызов на сокет, который не готов к отправке или больше не открыт, может вызвать ошибку Не запущено Error.
Если multicastInterface не может быть разобран в IP, то возникает ошибка EINVAL System Error.
В IPv4, если multicastInterface — допустимый адрес, но не соответствует ни одному интерфейсу, или если адрес не соответствует семейству, возникает ошибка System Error, например, EADDRNOTAVAIL или EPROTONOSUP.
В IPv6 большинство ошибок с указанием или пропуском scope приведут к тому, что сокет будет продолжать использовать (или возвращаться к) выбору системного интерфейса по умолчанию.
Адрес семейства сокета ANY (IPv4 '0.0.0.0' или IPv6 '::') может быть использован, чтобы вернуть контроль над сокетом по умолчанию для исходящего интерфейса системе для будущих пакетов многоадресной рассылки.
socket.setMulticastLoopback(flag)
-
flag<логическое>
Устанавливает или очищает опцию сокета IP_MULTICAST_LOOP. При установке в true пакеты многоадресной рассылки также будут приниматься на локальном интерфейсе.
socket.setMulticastTTL(ttl)
-
ttl<число> Целое число.
Устанавливает опцию сокета IP_MULTICAST_TTL. Хотя TTL обычно означает «Время жизни», в данном контексте он определяет количество IP-ходов, которые пакет разрешено пройти, в частности, для трафика многоадресной рассылки. Каждый маршрутизатор или шлюз, передающий пакет, уменьшает TTL. Если TTL уменьшится до 0 маршрутизатором, он не будет передан.
Аргумент, переданный функции socket.setMulticastTTL(), представляет собой число хопов от 0 до 255. Значение по умолчанию на большинстве систем — 1, но может отличаться.
socket.setTTL(ttl)
-
ttl<число> Целое число.
Устанавливает опцию сокета IP_TTL. Хотя TTL обычно означает «Время жизни», в данном контексте он определяет количество IP-ходов, которые пакет разрешено пройти. Каждый маршрутизатор или шлюз, передающий пакет, уменьшает TTL. Если TTL уменьшится до 0 маршрутизатором, он не будет передан. Изменение значений TTL обычно выполняется для сетевых зондирований или при многоадресной рассылке.
Аргумент для socket.setTTL() — это количество переходов между 1 и 255. По умолчанию на большинстве систем значение равно 64, но может изменяться.
socket.ref()
По умолчанию при привязке сокета процесс Node.js будет заблокирован от выхода, пока сокет открыт. Метод socket.unref() может быть использован для исключения сокета из подсчёта ссылок, который удерживает процесс Node.js активным. Метод socket.ref() добавляет сокет обратно в подсчёт ссылок и восстанавливает стандартное поведение.
Вызов socket.ref() несколько раз не повлияет на результат.
Метод socket.ref() возвращает ссылку на сокет, что позволяет цепочку вызовов.
socket.unref()
По умолчанию при привязке сокета процесс Node.js будет заблокирован от выхода, пока сокет открыт. Метод socket.unref() может быть использован для исключения сокета из подсчёта ссылок, который удерживает процесс Node.js активным, что позволит процессу выйти, даже если сокет всё ещё прослушивает.
Вызов socket.unref() несколько раз не повлияет на результат.
Метод socket.unref() возвращает ссылку на сокет, что позволяет цепочку вызовов.
Изменение асинхронного поведения socket.bind()
Начиная с Node.js v0.10, dgram.Socket#bind() изменился на асинхронную модель выполнения. Код, предполагающий синхронное поведение, как в следующем примере:
const s = dgram.createSocket('udp4');
s.bind(1234);
s.addMembership('224.0.0.114');
Должен быть изменён на передачу функции обратного вызова в функцию dgram.Socket#bind():
const s = dgram.createSocket('udp4');
s.bind(1234, () => {
s.addMembership('224.0.0.114');
});
dgram функции модуля
dgram.createSocket(options[, callback])
-
options<Объект> -
callback<Функция> Прикреплена как обработчик событий'message'. - Возвращает: <dgram.Сокет>
Создаёт объект dgram.Socket. Аргумент options — это объект, который должен содержать поле type со значением udp4 или udp6 и необязательное булево поле reuseAddr.
Когда reuseAddr равно true, socket.bind() будет повторно использовать адрес, даже если другой процесс уже привязал сокет к нему. reuseAddr по умолчанию равно false. Необязательная функция callback добавляется как обработчик событий 'message'.
После создания сокета, вызов socket.bind() укажет сокету начать прослушивание сообщений датаграмм. Когда address и port не передаются в socket.bind(), метод привяжет сокет к адресу «все интерфейсы» на случайном порту (правильно работает для сокетов udp4 и udp6). Связанный адрес и порт можно получить, используя socket.address().address и socket.address().port.
dgram.createSocket(type[, callback])
-
type<строка> - либо 'udp4', либо 'udp6'. -
callback<Функция> - Прикреплена как обработчик событий'message'. - Возвращает: <dgram.Сокет>
Создаёт объект dgram.Socket указанного типа type. Аргумент type может быть udp4 или udp6. Необязательная функция callback может быть передана и будет добавлена в качестве обработчика событий 'message'.
После создания сокета, вызов socket.bind() укажет сокету начать прослушивание сообщений датаграмм. Когда address и port не передаются в socket.bind(), метод привяжет сокет к адресу «все интерфейсы» на случайном порту (правильно работает для сокетов udp4 и udp6). Связанный адрес и порт можно получить, используя socket.address().address и socket.address().port.
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v6.x/docs/api/dgram.html