Skip to content

walasek/node-udp-messaging

Repository files navigation

Reliable messaging over UDP with holepunching.


Goal

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.

Installation

Node >=8.9.0 is required.

npm install --save udp-messaging

To perform tests use:

cd node_modules/udp-messaging
npm i
npm t

Usage

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();
})();

Example application

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]

Performance

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

Contributing

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/or Promise where possible.
  • Features must be properly tested.
  • New methods must be properly documented with jscode style comments.

About

Messaging over UDP for Node.js

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •