1.3.0 • Published 6 years ago

@dchowitz/webrtc-datachannel v1.3.0

Weekly downloads
1
License
MIT
Repository
github
Last release
6 years ago

webrtc-datachannel

A simple abstraction over WebRTC datachannels.

Signal server included.

Still in its early stage.

Runs in browsers, Node.js and React Native apps.

Install

npm install @dchowitz/webrtc-datachannel

Usage

Peers A and B want to exchange arbitrary data with each other. By some other means (your application logic), they have agreed upon a unique identifier for their datachannel connection.

const peerA = await datachannel(
  "CHANNELID",
  {
    signalServerUrl: "http://localhost:3333"
  },
  data => {
    // do something with incoming data
  }
);

// ... somewhere else peer B
const peerB = await datachannel(
  "CHANNELID",
  {
    /*...config*/
  },
  data => {
    // do something with incoming data
  }
);

// when channel is ready, both peers can send to each other
await peerA.send("hello from A");

// ... somewhere else
await peerB.send("hi back");

Non-browser environments

webrtc-datachannel tries to obtain the WebRTC API objects RTCPeerConnection, RTCSessionDescription and RTCIceCandidate from the global object and complains otherwise.

If you're in Node.js or React Native, you can provide a WebRTC implementation this way:

// in Node.js
const wrtc = require("wrtc");

// in React Native
const wrtc = require("react-native-webrtc");

// ...
const peer = await datachannel("CHANNELID", {
  signalServerUrl: "...",
  wrtc // an object: { RTCPeerConnection, RTCSessionDescription, RTCIceCandidate }
});

Configuration

webrtc-datachannel uses a public Google STUN server by default. You can override the default behavior by providing a rtcConfig object in the configuration, e.g.:

const peer = await datachannel("CHANNELID", {
  signalServerUrl: "...",
  rtcConfig: {
    iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
  }
});

The rtcConfig object follows the RTCConfiguration in the W3C specification. In the iceServers array you can pass the STUN and TURN servers to be used when establishing a connection with a remote peer. While a STUN server alone is able to establish connections for most network situations, a TURN (relay) server is required for peers behind very restrictive firewalls or in other specific network situations. Due to their nature as a data relay and costs associated with this, TURN servers are not for free. You can run your own (e.g. coturn) or pay for one.

A configuration with both STUN and TURN servers looks like this:

{
  "iceServers": [
    { "urls": "stun:stun.l.google.com:19302" },
    {
      "urls": "turn:your.turnserver.com",
      "username": "turnuser",
      "credential": "turnpassword"
    }
  ]
}

References

Limitations

Currently, max. message size is 64KB. This is to prevent chunking of big messages on receiver side. If you want to support messages of arbitrary side, you have to implement a protocol on top of webrtc-datachannel. This is planned for the future.

Only strings, typed arrays and buffers can be send. Sending arbitrary JS objects is not supported yet.

Observations

Messages bigger than 256 kB get splitted into chunks of 256 kB in nodejs. The receiver is responsible for reassembling.

Typed arrays and buffers arrive as ArrayBuffer on receiver side. Most probably caused by channel.binaryType = 'arraybuffer'.

Strings arrive as strings.

Data channel works fine with really big string messages (tested with messages of up to 256 MB).

Data channel closes when sending medium-sized messages (up to 8 MB of type ArrayBuffer) in high frequency. We have to respect the channel buffer (see channel.bufferedAmount). Sample shows how to monitor and handle accordingly.

Data channel closes when sending two laaarge messages (256 MB). Most likely due to buffer overflow.

Once a channel closes (because of some error or due to channel.close()) there is no way to reopen it.

A channel state of closed on one side of the channel doesn't necessarily mean that the state of the remote channel side is closed as well. In my experiments, when one side got an error and was closed, the other side showed still open.

Event onbufferedamountlow never fires, but should...

Open Points

Task Implement retries for send(). If the channel on one side closes, then we have to create a new one. The other side of the channel has to close the old one on datachannel event, if existing. We can also check the channel state before sending any messages and act accordingly. The abstraction for send() should return a Promise.

Task Ensure that a send message always equals the received message, or put another way, that no chunking occurs. Since our data channel abstraction is still low-level, a custom (use case specific) protocol on top of it has to handle chunking and reassembling. For that, the send() implementation must reject messages greater than a certain size, e. g. 64 KB (see Channel message size limitations).

Q What happens if the signalling resp. connection state changes, e. g. the address of one peer? Will the data channel be closed on both ends or is such situation handled transparently by WebRTC without affecting the current datachannel instance at all?

License

MIT. Copyright (c) Denny Christochowitz