node-datachannel
node-datachannel copied to clipboard
Intermittent data loss
If a datachannel sends data and then immediately closes the connection:
while (...) {
dc.send(buf)
}
dc.close()
If it sends a lot of small messages then the remote does not always receive all of the data.
I've opened https://github.com/murat-dogan/node-datachannel/pull/374 which has a failing test showing the behaviour.
There's a codepen here which allows running the same code in a browser, it works in Chrome and Firefox Nightly so this appears to be a bug in node-datachannel or libdatachannel.
Here's a reproduction without using the polyfill API, if it's useful:
import { nodeDataChannel } from 'node-datachannel'
// Log Level
// nodeDataChannel.initLogger("Debug")
// increase `chunks` until `Peer2` no longer receives all of the messages
const chunks = 1024 * 10
// the number of successfully sent messages
let bytesSent = 0
// the number of successfully received messages
let receivedBytes = 0
// how many bytes to send in each message
const chunkSize = 1024
// how many bytes to send
const bytesToSend = chunks * chunkSize
const receivedAllBytes = Promise.withResolvers()
const peer1 = new nodeDataChannel.PeerConnection('Peer1', { iceServers: [] })
const peer2 = new nodeDataChannel.PeerConnection('Peer2', { iceServers: [] })
peer1.onLocalDescription((sdp, type) => {
peer2.setRemoteDescription(sdp, type)
})
peer1.onLocalCandidate((candidate, mid) => {
peer2.addRemoteCandidate(candidate, mid)
})
peer2.onLocalDescription((sdp, type) => {
peer1.setRemoteDescription(sdp, type)
})
peer2.onLocalCandidate((candidate, mid) => {
peer1.addRemoteCandidate(candidate, mid)
})
const start = Date.now()
// necessary to stop dc from being garbage collected
let peer2Dc
peer2.onDataChannel((dc) => {
peer2Dc = dc
console.log('Peer2 Got DataChannel', dc.getLabel())
dc.onMessage((buf) => {
receivedBytes += buf.byteLength
if (receivedBytes === bytesToSend) {
receivedAllBytes.resolve()
}
})
dc.onClosed(() => {
if (receivedBytes !== bytesSent) {
receivedAllBytes.reject(new Error(`Peer2 channel closed after only receiving ${receivedBytes}/${bytesToSend} bytes`))
}
peer2Dc = null
})
})
const dc = peer1.createDataChannel('test channel')
dc.onOpen(async () => {
for (let i = 0; i < chunks; i++) {
dc.sendMessageBinary(Uint8Array.from(new Array(chunkSize).fill(0)))
bytesSent++
}
console.info('Peer1 sent all bytes, closing channel')
dc.close()
})
receivedAllBytes.promise
.then(() => {
console.log(`Peer2 received all ${bytesToSend} bytes after ${Date.now() - start} ms`)
})
.finally(() => {
peer1.close()
peer2.close()
nodeDataChannel.cleanup()
})