当您需要通过 UDP 传输数据时,您会面临其局限性:数据必须以小块的形式传输以适应 MTU,数据包可能会在没有通知的情况下丢失、重复或顺序错误。
uTP(微传输协议)是由 torrent 人们发明的,用于通过 UDP 安全地传输文件。基本上,它向 UDP 添加了 TCP 功能:丢失的数据包会自动重传、保证顺序、拒绝重复。
该库通过 UDP 实现 uTP,因此当连接时,您会收到一个套接字对象,其行为与 Node.js tcp 套接字非常相似。它在接收数据时发出“数据”,它有 .write() 方法供您发送缓冲区。与 TCP 套接字非常相似,这是一个 Node.js 流。
然而,该库可能与其他 uTP 实现不兼容(因此您需要在两个对等点上使用这个完全相同的库),因为它添加了以下功能:类的同一实例可以用作服务器和服务器。客户端同时在同一端口上。因此,您可以创建一个节点,将其绑定到端口,同时开始侦听传入连接并从中建立传出连接。
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');
});
这里使用的另一种技术是 UDP 打洞。
当服务器和/或客户端位于 NAT 之后时,它们通常没有可绑定的 Internet IP 地址来接收传入连接。
UDP 打洞欺骗防火墙为其用户打开一个临时漏洞,因此 NAT 设备上的端口将绑定到 LAN 内的服务器/客户端的端口。
为了使其正常工作,服务器和客户端都必须使用第三方服务器来查找彼此的 NATed IP 地址并协调打孔尝试(必须在服务器和客户端上同时完成)。
但是,当连接建立后,不再需要第三方服务器,也不再用作中继,所有数据都直接在该 NAT 服务器和客户端之间传输。
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
});
}
});
请参阅example/目录中的完整打孔示例。
同一个类可以用作服务器或客户端,语法如下:
选项如下:
{
bufferSize: 64, // number of packets
mtu: 1000, // bytes excluding uTP header
timeout: 5000, // ms
resend: 100, // ms
keepAlive: 1000, // ms
}
onConnection将传递单个参数 - 套接字。这是服务器的传入连接
节点可以处理的最大连接数的 Getter
传入连接数的 Getter
传出连接数的 Getter
返回在后台使用的标准 Node.js UDP 套接字。
套接字的绑定地址(与 Node.js UDP .address() 中的相同)
绑定到主机:端口并在完成后执行 onBound
开始尝试对主机:端口进行打孔,并在成功或没有尝试时运行回调。成功或失败作为第一个布尔参数传递给回调
将此节点转变为服务器,并在准备好接受传入连接时执行此回调
连接到主机上的服务器节点:端口并使用套接字对象作为单个参数执行回调
终止所有连接和节点,运行回调。
传递给 Node 构造函数和 .connect() 回调的 Socket 对象是一个在接收数据时发出“数据”的流。它有常用的方法:.write()、.end()等。
原始“utp”库由 @mafintosh 在 https://github.com/mafintosh/utp 创建。这是用现代 JavaScript 进行的重写,具有错误修复和附加功能,包括在同一端口上同时用作服务器和客户端以及 UDP 打洞支持。