Node.js, dgram.setBroadcast(флаг) не удается из-за "EBADF"

Я использую Node.js 0.6.9, и я пытаюсь отправить пакет широковещательной передачи датаграммы. Код:

var sys = require('util');
var net = require('net');

var dgram = require('dgram');
var message = new Buffer('message');
var client = dgram.createSocket("udp4");
client.setBroadcast(true);
client.send(message, 0, message.length, 8282, "192.168.1.255", function(err, bytes) {
  client.close();
});

Запуск кода:

$ node test.js
node.js:201
        throw e; // process.nextTick error, or 'error' event on first tick
              ^
Error: setBroadcast EBADF
    at errnoException (dgram.js:352:11)
    at Socket.setBroadcast (dgram.js:227:11)
    at Object.<anonymous> (/home/letharion/tmp/collision/hello.js:25:8)
    at Module._compile (module.js:444:26)
    at Object..js (module.js:462:10)
    at Module.load (module.js:351:32)
    at Function._load (module.js:310:12)
    at Array.0 (module.js:482:10)
    at EventEmitter._tickCallback (node.js:192:41)

Некоторые поисковые запросы показывают, что "EBADF" означает "аргумент сокета не является допустимым файловым дескриптором". Но я недостаточно понимаю проблему, чтобы это было полезно.

Ответ 1

Прежде всего, у вас, похоже, есть проблемы с пониманием формата stacktrace, поэтому давайте проясним его, прежде чем мы перейдем к фактической ошибке, которая здесь возникает.

Формат node.js Stacktrace

node.js:201
        throw e; // process.nextTick error, or 'error' event on first tick

Эта часть - это просто место, где внутренняя логика NodeJS забилась и выдает ошибку ниже:

Ниже приведена фактическая ошибка stacktrace, она показывает местоположение самая глубокая в стоп-кассе, поэтому переходим вниз в трассировку стека, привнося вверх в вызове hierachy, в конечном итоге приведет вас к точке вашего кода, где все началось.

Error: setBroadcast EBADF
    at errnoException (dgram.js:352:11)
    at Socket.setBroadcast (dgram.js:227:11)
    at Object.<anonymous> (/home/letharion/tmp/collision/hello.js:25:8)
    at Module._compile (module.js:444:26)
    at Object..js (module.js:462:10)
    at Module.load (module.js:351:32)
    at Function._load (module.js:310:12)
    at Array.0 (module.js:482:10)
    at EventEmitter._tickCallback (node.js:192:41)

Сначала он терпит неудачу в dgram.js on line 352, dgram.js - это внутренний модуль node.js, абстрагирующий код "низкого уровня". Строка 352 находится в функции, содержащей общую логику для возникновения ошибок.

Он был вызван в dgram.js in line 227 после неудачной проверки, которая завершает вызов обернутых родных UDP-сокетов setBroadcast.

Подняв еще один слой, мы получим ваш hello.js файл в строке 25 с вызовом client.setBroadcast(true);.

Остальное - это node.js код, полученный в результате начальной загрузки файла hello.js.

Фактическая ошибка

Ошибка, вызванная нативным кодом, который обертывает node.js, EBADF рассматривая это в сочетании с UDP дает нам:

EBADF
    The socket argument is not a valid file descriptor. 

Идя далее вниз в отверстие кролика node.js, мы попадаем в udp wrapper, который обертывает uv wrapper для фактической реализации C, в обертке uv мы находим:

/*
* Set broadcast on or off
*
* Arguments:
* handle UDP handle. Should have been initialized with
* `uv_udp_init`.
* on 1 for on, 0 for off
*
* Returns:
* 0 on success, -1 on error.
*/

Приведем нас к выводу, что ваш сокет еще не инициализирован.

В конце концов, привязка сокета через client.bind(8000) исправила отсутствующую инициализацию и запустила программу.

Ответ 2

Метод setBroadcast должен вызываться для события 'listening' или передаваться как обратный вызов в методе bind:

var socket = dgram.createSocket('udp4');

socket.on('listening', function(){
    socket.setBroadcast(true);
});

socket.bind(8000);

ИЛИ:

var socket = dgram.createSocket('udp4');

socket.bind(8000, undefined, function() {
    socket.setBroadcast(true);
});

Ответ 3

Кажется, что дескриптор файла создается только при связывании или при отправке, и он требуется перед setBroadcast. Вы можете вызвать client.bind() без параметра для привязки к случайному порту перед настройкой широковещательной передачи. Не беспокойтесь об использовании случайного порта, так как он "лениво" при использовании client.send в любом случае.

var sys = require('util');
var net = require('net');

var dgram = require('dgram');
var message = new Buffer('message');
var client = dgram.createSocket("udp4");
client.bind();
client.setBroadcast(true);
client.send(message, 0, message.length, 8282, "192.168.1.255", function(err, bytes) {
    client.close();
});