Передача сообщений OSC между двумя компьютерами через WAN через UDP и Node.js

Я пытаюсь написать некоторый минималистский клиент-серверный код для передачи сообщений OSC между двумя компьютерами, которые находятся в разных локальных сетях. В частности, моей конечной целью является отправка и получение патчей MAX в Ableton, используя функции MAX UDPSend и UDPReceive, которые просто прослушивают локальные порты. Вот код сервера, на который я отправляю MIDI-модуль OSC Ableton Ableton. Это правильно получает вывод от первого патча MAX компьютера, некоторые MIDI-данные из канала.

var osc = require('osc-min'),
dgram = require('dgram');
var remotes = [];
var FileReader = require('fs');

// listen for OSC messages and print them to the console
var udp = dgram.createSocket('udp4', function(msg, rinfo) {
  try {
    var oscmsg = osc.fromBuffer(msg);
    console.log(oscmsg);
    remotes.forEach(function(remote, index, array) {
        udp.send(msg, 0, oscmsg.length, 10000, remote);
        console.log('Sent OSC message to %s:%d', remote, 10000);
    });
  } catch (err) {
    console.log(err);
  }

});

FileReader.readFile('ips.txt', 'utf8', function(err, data) {
    // By lines
    var lines = data.split('\n');
    for(var line = 0; line < lines.length; line++){
        remotes.push(lines[line]);
    }
});

udp.bind(9998);
console.log('Listening for OSC messages on port 9998');

Сервер правильно получает сообщение OSC, как это:

{ address: '/Note1',
args: [ { type: 'integer', value: 60 } ],
oscType: 'message' }

Затем я хочу передать сообщение компьютера 1 на мой удаленный сервер через порт 9998 остальным компьютерам, которые прослушиваю через порт 10000. (Сервер является экземпляром Amazon EC2 Ubuntu.) Общедоступные IP-адреса этих компьютеров находятся в файле ips..текст.

На принимающих компьютерах (тестирование в Windows) я настроил Node-клиент для получения данных через общедоступный IP-адрес и передачи их на локальный хост, чтобы патч MAX мог их видеть. Таким образом, любые сообщения, отправленные на номер 10000 на этом компьютере, будут отправлены на номер 127.0.0.1:9999.

var osc = require('osc-min'),
    dgram = require('dgram'),
    remote;

// listen for OSC messages and print them to the console
var udp = dgram.createSocket('udp4', function(msg, rinfo) {

  try {
    var oscmsg = osc.fromBuffer(msg);
    console.log(oscmsg);
    send(msg);
  } catch (err) {
    console.log('Could not decode OSC message');
  }

});

function send(oscMessage) {

  udp.send(oscMessage, 0, oscMessage.length, 9999, "127.0.0.1");
  console.log('Sent OSC message to %s:%d', "127.0.0.1", 9999);

}    
udp.bind(10000);
console.log('Listening for OSC messages on port 10000');

Проблема в том, что сообщение сервера никогда не достигает клиента. Как только сервер получает сообщение, он производит вывод как таковой:

Listening for OSC messages on port 9998
{ address: '/Velocity1',
args: [ { type: 'integer', value: 100 } ],   oscType: 'message' }
Sent OSC message to client-public-ip:10000

Но клиент просто зависает в своем начальном состоянии прослушивания

Listening for OSC messages on port 10000

Я подумал, что это может быть проблемой с моим брандмауэром, и я позаботился о том, чтобы включить UDP специально через порт и во всех задействованных приложениях, но безрезультатно. Я надеюсь избежать использования HTTPRequests. Буду очень признателен за любые предложения.

Изменить: я создал диаграмму, чтобы помочь визуализировать то, что должно происходить между этими тремя компьютерами. Пунктирные прямоугольники представляют локальную сеть, в которой находится компьютер (все они находятся в разных сетях). Надеюсь, это немного поможет.

2 ответа

Решение

Я нашел самый простой способ добиться того, чтобы это работало между различными локальными и глобальными сетями, - это прокси-запросы UDP и отправлять их на сервер Echo с помощью веб-сокетов. Вот пример:

Эхо-сервер (WAN)

"use strict";

const WebSocket = require('ws');
const port = process.env.PORT || 3000;
const wss = new WebSocket.Server({ port: port });

// Broadcast to all
wss.broadcast = function broadcast(data) {
    wss.clients.forEach(function each(client) {
        if (client.readyState === WebSocket.OPEN) {
            client.send(data);
        }
    });
};

wss.on('connection', function connection(ws, req) {
    const ip = req.connection.remoteAddress;
    console.log('connecting to', ip);

    ws.on('message', function incoming(data) {
        // Broadcast to everyone else.
        wss.clients.forEach(function each(client) {
            if (client !== ws && client.readyState === WebSocket.OPEN) {
                client.send(data);
            }
        });
    });
});

UDP-сервер / клиент WebSocket (локальный)

const Buffer = require('buffer').Buffer;
const dgram = require('dgram');
const WebSocketServer = require('ws').Server;

const port = process.env.PORT || 3000;

const wss = new WebSocketServer({port});

//The ip port of the UDP server
var SERVER_IP = '127.0.0.1'
var SERVER_PORT = 11000

wss.on('connection', function(ws) {
    //Create a udp socket for this websocket connection
    let udpClient = dgram.createSocket('udp4');

    //When a message is received from udp server send it to the ws client
    udpClient.on('message', function(msg, rinfo) {
        ws.send(msg.toString());
    });

    //When a message is received from ws client send it to UDP server.
    ws.on('message', function(message) {
        var msgBuff = new Buffer(message);
        udpClient.send(msgBuff, 0, msgBuff.length, SERVER_PORT, SERVER_IP);
    });
});

тестирование

Вы можете настроить подключение Ableton к локальному UDP-серверу, в этом примере адрес по умолчанию будет выглядеть примерно так: udp://localhost:11000.

Решение Дэниела использовать WebSockets правильное. Однако мне также нужно было обязательно подключать клиентов через DNS-имя, а не публичный IP-адрес. Например:

const WebSocketClient = require('websocket').client;
const port = process.env.PORT || 3000;
const wsc = new WebSocketClient();
wsc.connect('ws://ec2.compute-1.amazonaws.com:4000/', 'echo-protocol');

В отличие от:

wsc.connect('ws://1.2.3.4:4000/', 'echo-protocol');

Подробнее об этой проблеме здесь.

Я также использовал модуль npm 'websocket' вместо 'ws', но я не уверен, что это имело значение.

Другие вопросы по тегам