Reliable messaging over UDP with holepunching.
Peer-to-Peer communication is a tricky subject when your peers are hidden behind routers with NAT tables. This project aims at delivering tools to:
- Allow sending and receiving whole messages of arbitrary size
- Message delivery on best-effort basis (whole message received or nothing at all)
- Allow NAT holepuching
This project DOES NOT provide the following:
- A session mechanism
- Anti-spoofing (datagram source is not verified, applications using this library might be victims of a DDOS attack if proper rate-limiting is not implemented)
- Sending streams
Remarks:
- Big messages are more sensitive to network delays and packet loss. Sending multiple small messages is much more reliable.
- If too many messages are created at the same time then due to UDP's nature packets will get lost. An internal timer waits before retry.
- The maximum size of a message is 2^32-1 bytes, which will probably not fit into RAM anyways. Use with big-data might require a higher protocol layer on top of this library to allow data streaming.
Node >=8.9.0
is required.
npm install --save udp-messaging
To perform tests use:
cd node_modules/udp-messaging
npm i
npm t
Beware this project is still in development. There may be serious bugs or performance issues over time.
Documentation is available here.
(async () => {
// Create a reliable UDP socket
const P2PSocket = require('udp-messaging');
const p2p = new P2PSocket({ port: 12345 });
await p2p.bind();
// This promise resolves when the remote end receives the message.
// It rejects if the remote end did not respond at all.
await p2p.sendMessage(Buffer.from('Hey there!'), '10.8.128.1', 12345);
// The Socket is an EventEmitter.
p2p.on('message', (data, address, port) => {
console.log(`Message from %s:%d - %o`, address, port, data);
});
// Execute holepunching (get an address and port that another peer over the internet can use to reach this peer)
const hole = await p2p.discoverSelf();
// Interrupt all pending messages being sent and received
// Note: all sendMessage Promises are resolved, not rejected!
p2p.close();
})();
The p2p-discovery file contains an example application that allows maintaining a p2p mesh. Peers share addresses of other known peers with each other. The result of node examples/p2p-discovery.js -h
is shown below. An additional debug
tag is defined: udp-messaging:discovery
.
Run a P2P discovery node
Options:
--help, -h Print help
--local_port, -p Use a specific local port, 0 for random [default: 0]
--remote_ip, -t Define a known remote peer [default: "127.0.0.1"]
--remote_port, -r Define remote peer port
--stun_xval, -s Interval between STUN requests [ms] [default: 60000]
The testsuite prints out some basic bandwidth information (tested on localhost with simulated packet loss and network delay). Keep in mind bandwidth results depend a lot on the network status, active processes and many other variables. Performance can also be checked with the example iperf script.
✓ Testing file ./tests/E2E.test.js
✓ E2E non lossy, non delayed test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 1.929 sec. Bandwidth: 1061.6899948159669 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 0.017 sec. Bandwidth: 7710 Kb/s
✓ E2E 15% loss, non delayed test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 1.549 sec. Bandwidth: 1322.1433182698515 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 1.812 sec. Bandwidth: 72.33443708609272 Kb/s
✓ E2E 30% loss, non delayed test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 1.093 sec. Bandwidth: 1873.7419945105214 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 4.86 sec. Bandwidth: 26.969135802469136 Kb/s
✓ E2E non lossy, 500 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 1.135 sec. Bandwidth: 1804.4052863436123 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 0.03 sec. Bandwidth: 4369 Kb/s
✓ E2E 15% loss, 500 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 0.918 sec. Bandwidth: 2230.9368191721132 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 1.105 sec. Bandwidth: 118.61538461538461 Kb/s
✓ E2E 30% loss, 500 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 0.7 sec. Bandwidth: 2925.714285714286 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 5.234 sec. Bandwidth: 25.042032862055787 Kb/s
✓ E2E non lossy, 1000 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 0.679 sec. Bandwidth: 3016.20029455081 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 0.021 sec. Bandwidth: 6241.428571428572 Kb/s
✓ E2E 15% loss, 1000 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 0.662 sec. Bandwidth: 3093.6555891238672 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 3.219 sec. Bandwidth: 40.717614165890026 Kb/s
✓ E2E 30% loss, 1000 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 0.659 sec. Bandwidth: 3107.7389984825495 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 4.616 sec. Bandwidth: 28.39471403812825 Kb/s
✓ E2E non lossy, 1500 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 0.695 sec. Bandwidth: 2946.7625899280574 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 0.013 sec. Bandwidth: 10082.307692307691 Kb/s
✓ E2E 15% loss, 1500 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 0.733 sec. Bandwidth: 2793.9972714870396 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 1.505 sec. Bandwidth: 87.08970099667773 Kb/s
✓ E2E 30% loss, 1500 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 1.168 sec. Bandwidth: 1753.4246575342465 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 5.119 sec. Bandwidth: 25.604610275444422 Kb/s
✓ E2E non lossy, 2000 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 0.757 sec. Bandwidth: 2705.4161162483488 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 0.014 sec. Bandwidth: 9362.142857142857 Kb/s
✓ E2E 15% loss, 2000 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 0.878 sec. Bandwidth: 2332.5740318906605 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 1.628 sec. Bandwidth: 80.50982800982801 Kb/s
✓ E2E 30% loss, 2000 ms delay test suite:
✓ E2E #1 - can exchange small messages over stunned ports
✓ E2E #2 - can exchange small messages full-duplex over stunned ports
✓ E2E #2 Summary: Time elapsed 1.071 sec. Bandwidth: 1912.2315592903828 Kb/s
✓ E2E #3 - can exchange large messages half-duplex over stunned ports
✓ E2E #3 Summary: Time elapsed 5.516 sec. Bandwidth: 23.76178390137781 Kb/s
✓ ok
The source is documented with JSDoc. To generate the documentation use:
npm run docs
Extra debugging information is printed using the debug
module:
DEBUG=udp-messaging:* npm t
The documentation will be put in the new docs
directory.
To introduce an improvement please fork this project, commit changes in a new branch to your fork and add a pull request on this repository pointing at your fork. Please follow these style recommendations when working on the code:
- Use tabs (yup).
- Use
async
/await
and/orPromise
where possible. - Features must be properly tested.
- New methods must be properly documented with
jscode
style comments.