Lorsque vous devez transmettre des données via UDP, vous êtes confronté à ses limites : les données doivent être transmises en petits morceaux pour tenir dans la MTU, les paquets peuvent être perdus sans notification, être dupliqués ou dans le mauvais ordre.
uTP (micro transport protocol) a été inventé par des utilisateurs de torrent pour transmettre des fichiers en toute sécurité via UDP. En gros, il ajoute des fonctionnalités TCP à UDP : les paquets perdus sont automatiquement retransmis, l'ordre est garanti, les doublons rejetés.
Cette bibliothèque implémente uTP sur UDP. Ainsi, une fois connecté, vous recevez un objet socket qui se comporte un peu comme un socket TCP Node.js. Il émet des « données » lors de la réception de données, il a la méthode .write() pour vous permettre d'envoyer un tampon. Tout comme le socket TCP, il s'agit d'un flux Node.js.
La bibliothèque, cependant, peut ne pas être compatible avec d'autres implémentations uTP (vous devez donc utiliser cette même bibliothèque sur les deux pairs) car elle ajoute la fonctionnalité suivante : la même instance d'une classe peut être utilisée à la fois comme serveur et comme serveur. client en même temps sur le même port. Vous pouvez donc créer un nœud, le lier à un port et en même temps commencer à écouter les connexions entrantes et également établir des connexions sortantes à partir de celui-ci.
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');
});
Une autre technique utilisée ici est la perforation UDP.
Lorsque le serveur et/ou le client sont derrière NAT, ils n'ont normalement pas d'adresse IP Internet à laquelle se lier afin de recevoir les connexions entrantes.
La perforation UDP incite les pare-feu à ouvrir un trou temporaire pour son utilisateur, de sorte qu'un port du périphérique NAT devient lié au port du serveur/client à l'intérieur du réseau local.
Pour que cela fonctionne, le serveur et le client doivent utiliser un serveur tiers pour connaître les adresses IP NATées de chacun et coordonner la tentative de pointage (cela doit être effectué simultanément sur le serveur et sur le client).
Mais lorsque la connexion est établie le serveur tiers n'est plus nécessaire et il n'est jamais utilisé comme relais, toutes les données sont transmises directement entre ce serveur NATé et le client.
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
});
}
});
Veuillez consulter l'exemple complet de perforation dans le répertoire example/ .
La même classe peut être utilisée comme serveur ou comme client, la syntaxe est la suivante :
les options sont les suivantes :
{
bufferSize: 64, // number of packets
mtu: 1000, // bytes excluding uTP header
timeout: 5000, // ms
resend: 100, // ms
keepAlive: 1000, // ms
}
onConnection recevra un seul argument - le socket. Ceci est les connexions entrantes du serveur
Getter pour le nombre maximum de connexions que le nœud peut gérer
Getter pour le nombre de connexions entrantes
Getter pour le nombre de connexions sortantes
Renvoie le socket UDP Node.js standard qui est utilisé sous le capot.
Adresse liée du socket (la même que dans Node.js UDP .address())
Liez-vous à l'hôte:port et exécutez onBound une fois terminé
Commencez à envoyer des tentatives vers l'hôte:port et exécutez un rappel lorsqu'il reste des tentatives réussies ou qu'il ne reste plus aucune tentative. Le succès ou l'échec est transmis au rappel en tant que premier paramètre booléen
Transformez ce nœud en serveur et exécutez ce rappel lorsque vous êtes prêt à accepter les connexions entrantes
Connectez-vous à un nœud de serveur sur host:port et exécutez le rappel avec l'objet socket comme paramètre unique
Terminez toutes les connexions et le nœud, exécutez le rappel.
L'objet Socket transmis au constructeur Node et les rappels .connect() sont un flux émettant des « données » lors de la réception de données. Il dispose des méthodes habituelles : .write(), .end(), etc.
La bibliothèque 'utp' originale a été créée par @mafintosh sur https://github.com/mafintosh/utp. Il s'agit d'une réécriture en JavaScript moderne avec correction de bogues et fonctionnalités supplémentaires, notamment l'utilisation simultanée en tant que serveur et client sur le même port et la prise en charge de la perforation UDP.