當您需要透過 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 打洞支援。