Wenn Sie Daten über UDP übertragen müssen, stoßen Sie auf dessen Grenzen: Daten müssen in kleinen Blöcken übertragen werden, damit sie in die MTU passen, Pakete könnten ohne Benachrichtigung verloren gehen, dupliziert oder in der falschen Reihenfolge ankommen.
uTP (Micro Transport Protocol) wurde von Torrent-Leuten erfunden, um Dateien sicher über UDP zu übertragen. Im Grunde fügt es UDP TCP-Funktionen hinzu: Verlorene Pakete werden automatisch erneut übertragen, die Reihenfolge ist garantiert, Duplikate werden abgelehnt.
Diese Bibliothek implementiert uTP über UDP, sodass Sie bei einer Verbindung ein Socket- Objekt erhalten, das sich ähnlich wie ein Node.js-TCP-Socket verhält. Es gibt „Daten“ aus, wenn es Daten empfängt, und verfügt über die Methode .write(), mit der Sie einen Puffer senden können. Ähnlich wie beim TCP-Socket handelt es sich hierbei um einen Node.js-Stream.
Die Bibliothek ist jedoch möglicherweise nicht mit anderen uTP-Implementierungen kompatibel (Sie müssen also auf beiden Peers dieselbe Bibliothek verwenden), da sie die folgende Funktion hinzufügt: Dieselbe Instanz einer Klasse kann sowohl als Server als auch als Server verwendet werden Client zur gleichen Zeit am selben Port. Sie können also einen Knoten erstellen, ihn an einen Port binden und gleichzeitig damit beginnen, auf eingehende Verbindungen zu lauschen und auch ausgehende Verbindungen von ihm herzustellen.
npm install --save utp-punch
const Node = require('utp-punch');
let server = new Node(socket => {
console.log('server: socket connected');
socket.on('data', data => {
console.log(`server: received '${data.toString()}'`);
socket.write('world');
socket.end();
});
socket.on('end', () => {
console.log('server: socket disconnected');
server.close(); // this is how you terminate node
});
});
server.bind(20000, '127.0.0.1'); // bind to port 20000
server.listen( // run
() => console.log('server: ready')
);
let client = new Node();
client.bind(); // bind to any port
client.connect(20000, '127.0.0.1', socket => {
console.log('client: socket connected');
socket.on('data', data => console.log(`client: received '${data.toString()}'`));
socket.on('end', () => {
console.log('client: socket disconnected');
client.close(); // this is how you terminate node
});
socket.write('hello');
});
Eine weitere Technik, die hier zum Einsatz kommt, ist das UDP-Lochstanzen.
Wenn sich Server und/oder Client hinter NAT befinden, verfügen sie normalerweise nicht über eine Internet-IP-Adresse, an die sie sich binden können, um eingehende Verbindungen zu empfangen.
Beim UDP-Hole-Punching werden Firewalls dazu gebracht, eine vorübergehende Lücke für den Benutzer zu öffnen, sodass ein Port auf dem NAT-Gerät an den Port des Servers/Clients im LAN gebunden wird.
Damit es funktioniert, müssen sowohl Server als auch Client einen Drittanbieter-Server nutzen, um die NAT-IP-Adressen des jeweils anderen herauszufinden und den Punching-Versuch zu koordinieren (dies muss gleichzeitig auf dem Server und auf dem Client erfolgen).
Wenn die Verbindung jedoch hergestellt ist, wird der Drittanbieter-Server nicht mehr benötigt und er wird nie als Relais verwendet, alle Daten werden direkt zwischen diesem NAT-Server und dem Client übertragen.
const Node = require('utp-punch');
let server = new Node();
server.bind(20000);
let client = new Node();
client.bind(30000); // client needs dedicated port
// just as the server
// both server and client must contact a third party server
// which will report their peer NATed address and port to them
let serverAddress, serverPort;
let clientAddress, clientPort;
// up to ten punches:
server.punch(10, clientPort, clientAddress, success => {
// if success is true hole is punched from our side
// nothing to do here as the client will try
// to connect normally when he is also successful
});
client.punch(10, serverPort, serverAddress, success => {
if (success) {
client.connect(serverPort, serverAddress, socket => {
// if the server had also been successful in punching
// this will succeed
});
client.on('timeout', () => {
// if the server had failed in punching we won't be
// able to connect
});
}
});
Das vollständige Lochstanzbeispiel finden Sie im Verzeichnis example/ .
Dieselbe Klasse kann als Server oder als Client verwendet werden. Die Syntax lautet wie folgt:
Optionen sind die folgenden:
{
bufferSize: 64, // number of packets
mtu: 1000, // bytes excluding uTP header
timeout: 5000, // ms
resend: 100, // ms
keepAlive: 1000, // ms
}
onConnection wird ein einziges Argument übergeben – der Socket. Dies sind die eingehenden Verbindungen des Servers
Getter für die maximale Anzahl von Verbindungen, die der Knoten verarbeiten kann
Getter für die Anzahl eingehender Verbindungen
Getter für die Anzahl der ausgehenden Verbindungen
Gibt den Standard-UDP-Socket von Node.js zurück, der unter der Haube verwendet wird.
Gebundene Adresse des Sockets (die gleiche wie in Node.js UDP .address())
Binden Sie an host:port und führen Sie onBound aus, wenn Sie fertig sind
Beginnen Sie mit dem Senden von Versuchen an den Host:Port und führen Sie einen Rückruf aus, wenn entweder erfolgreiche oder keine Versuche mehr vorhanden sind. Erfolg oder Misserfolg werden als erster boolescher Parameter an den Rückruf übergeben
Verwandeln Sie diesen Knoten in einen Server und führen Sie diesen Rückruf aus, wenn er bereit ist, eingehende Verbindungen anzunehmen
Stellen Sie eine Verbindung zu einem Serverknoten auf Host:Port her und führen Sie einen Rückruf mit dem Socket-Objekt als Einzelparameter aus
Beenden Sie alle Verbindungen und den Knoten und führen Sie einen Rückruf aus.
Das an den Knotenkonstruktor und .connect()-Rückrufe übergebene Socket-Objekt ist ein Stream, der beim Empfang von Daten „Daten“ ausgibt. Es verfügt über die üblichen Methoden: .write(), .end() usw.
Die ursprüngliche „utp“-Bibliothek wurde von @mafintosh unter https://github.com/mafintosh/utp erstellt. Hierbei handelt es sich um eine Neufassung in modernem JavaScript mit Fehlerbehebung und zusätzlichen Funktionen, einschließlich der gleichzeitigen Verwendung als Server und Client am selben Port und UDP-Hole-Punching-Unterstützung.