Передача сообщений 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', но я не уверен, что это имело значение.