Quando você precisa transmitir dados por UDP, você enfrenta suas limitações: os dados devem ser transmitidos em pequenos pedaços para caber no MTU, os pacotes podem ser perdidos sem notificação, duplicados ou na ordem errada.
O uTP (protocolo de micro transporte) foi inventado por torrentistas para transmitir arquivos com segurança por UDP. Basicamente, ele adiciona recursos TCP ao UDP: pacotes perdidos são retransmitidos automaticamente, a ordem é garantida, duplicatas rejeitadas.
Esta biblioteca implementa uTP sobre UDP para que, quando conectado, você receba um objeto de soquete que se comporta de maneira semelhante a um soquete tcp do Node.js. Ele emite 'dados' ao receber dados, possui o método .write() para você enviar um Buffer. Muito parecido com o soquete TCP, este é um fluxo Node.js.
A biblioteca, no entanto, pode não ser compatível com outras implementações uTP (então você precisa usar esta mesma biblioteca em ambos os pares) porque adiciona o seguinte recurso: a mesma instância de uma classe pode ser usada tanto como servidor quanto como servidor. cliente ao mesmo tempo na mesma porta. Assim você pode criar um Node, vinculá-lo a uma porta e ao mesmo tempo começar a escutar conexões de entrada e também fazer conexões de saída a partir dele.
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');
});
Outra técnica usada aqui é a perfuração UDP.
Quando o servidor e/ou cliente estão atrás de NAT, eles normalmente não têm um endereço IP da Internet ao qual se conectar para receber conexões de entrada.
A perfuração UDP engana os firewalls para que abram uma brecha temporária para seu usuário, de modo que uma porta no dispositivo NAT fique vinculada à porta do servidor/cliente dentro da LAN.
Para que funcione, tanto o servidor quanto o cliente devem usar um servidor de terceiros para descobrir os endereços IP NAT um do outro e coordenar a tentativa de perfuração (deve ser feita simultaneamente no servidor e no cliente).
Mas quando a conexão é estabelecida, o servidor de terceiros não é mais necessário e nunca é usado como retransmissor, todos os dados são transmitidos diretamente entre este servidor NAT e o cliente.
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
});
}
});
Por favor, veja o exemplo completo de perfuração no diretório example/ .
A mesma classe pode ser usada como servidor ou como cliente, a sintaxe é a seguinte:
opções é o seguinte:
{
bufferSize: 64, // number of packets
mtu: 1000, // bytes excluding uTP header
timeout: 5000, // ms
resend: 100, // ms
keepAlive: 1000, // ms
}
onConnection receberá um único argumento - o soquete. Estas são as conexões de entrada do servidor
Getter para o número máximo de conexões que o Node pode manipular
Getter para o número de conexões de entrada
Getter para o número de conexões de saída
Retorna o soquete UDP Node.js padrão que é usado nos bastidores.
Endereço vinculado do soquete (o mesmo que em Node.js UDP .address())
Vincule ao host:port e execute onBound quando terminar
Comece a perfurar tentativas para host:port e execute o retorno de chamada quando nenhuma tentativa for bem-sucedida ou não houver mais tentativas. O sucesso ou a falha são passados para o retorno de chamada como o primeiro parâmetro booleano
Transforme este Node em um servidor e execute este callback quando estiver pronto para aceitar conexões de entrada
Conecte-se a um nó do servidor em host:port e execute o retorno de chamada com o objeto soquete como parâmetro único
Encerre todas as conexões e o nó, execute o retorno de chamada.
O objeto Socket passado para o construtor Node e os retornos de chamada .connect() são um fluxo que emite 'dados' ao receber dados. Possui os métodos usuais: .write(), .end(), etc.
A biblioteca 'utp' original foi criada por @mafintosh em https://github.com/mafintosh/utp. Esta é uma reescrita no JavaScript moderno com correção de bugs e recursos adicionais, incluindo o uso como servidor e cliente simultaneamente na mesma porta e suporte para perfuração UDP.