@bcjordan/node-datachannel
Version:
libdatachannel node bindings
228 lines (193 loc) • 7.21 kB
JavaScript
/* eslint-disable @typescript-eslint/no-var-requires */
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const nodeDataChannel = require('../../lib/index');
const WebSocket = require('ws');
const readline = require('readline');
// Init Logger
nodeDataChannel.initLogger('Debug');
// PeerConnection Map
const pcMap = {};
const dcArr = [];
// Local ID
const id = randomId(4);
// Message Size
const MESSAGE_SIZE = 65535;
// Buffer Size
const BUFFER_SIZE = MESSAGE_SIZE * 0;
// Args
const argv = yargs(hideBin(process.argv))
.option('disableSend', {
type: 'boolean',
description: 'Disable Send',
default: false,
})
.option('wsUrl', {
type: 'string',
description: 'Web Socket URL',
default: 'ws://localhost:8000',
})
.option('dataChannelCount', {
type: 'number',
description: 'Data Channel Count To Create',
default: 1,
}).argv;
// Disable Send
const disableSend = argv.disableSend;
if (disableSend) console.log('Send Disabled!');
// Signaling Server
const wsUrl = process.env.WS_URL || argv.wsUrl;
console.log(wsUrl);
const dataChannelCount = argv.dataChannelCount;
// Read Line Interface
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const ws = new WebSocket(wsUrl + '/' + id, {
perMessageDeflate: false,
});
console.log(`The local ID is: ${id}`);
console.log(`Waiting for signaling to be connected...`);
ws.on('open', () => {
console.log('WebSocket connected, signaling ready');
readUserInput();
});
ws.on('error', (err) => {
console.log('WebSocket Error: ', err);
});
ws.on('message', (msgStr) => {
msg = JSON.parse(msgStr);
switch (msg.type) {
case 'offer':
createPeerConnection(msg.id);
pcMap[msg.id].setRemoteDescription(msg.description, msg.type);
break;
case 'answer':
pcMap[msg.id].setRemoteDescription(msg.description, msg.type);
break;
case 'candidate':
pcMap[msg.id].addRemoteCandidate(msg.candidate, msg.mid);
break;
default:
break;
}
});
function readUserInput() {
rl.question('Enter a remote ID to send an offer: ', (peerId) => {
if (peerId && peerId.length > 2) {
rl.close();
console.log('Offering to ', peerId);
createPeerConnection(peerId);
let msgToSend = randomId(MESSAGE_SIZE);
for (let i = 1; i <= dataChannelCount; i++) {
let dcArrItem = {
dc: null,
bytesSent: 0,
bytesReceived: 0,
};
console.log('Creating DataChannel with label "test-' + i + '"');
dcArrItem.dc = pcMap[peerId].createDataChannel('test-' + i);
dcArrItem.dc.setBufferedAmountLowThreshold(BUFFER_SIZE);
dcArrItem.dc.onOpen(() => {
while (!disableSend && dcArrItem.dc.bufferedAmount() <= BUFFER_SIZE) {
dcArrItem.dc.sendMessage(msgToSend);
dcArrItem.bytesSent += msgToSend.length;
}
});
dcArrItem.dc.onBufferedAmountLow(() => {
while (!disableSend && dcArrItem.dc.bufferedAmount() <= BUFFER_SIZE) {
dcArrItem.dc.sendMessage(msgToSend);
dcArrItem.bytesSent += msgToSend.length;
}
});
dcArrItem.dc.onMessage((msg) => {
dcArrItem.bytesReceived += msg.length;
});
dcArr.push(dcArrItem);
}
// Report
let i = 0;
setInterval(() => {
let totalBytesSent = 0;
let totalBytesReceived = 0;
for (let j = 0; j < dcArr.length; j++) {
console.log(
`${j == 0 ? i + '#' : ''} DC-${j + 1} Sent: ${byte2KB(
dcArr[j].bytesSent,
)} KB/s / Received: ${byte2KB(dcArr[j].bytesReceived)} KB/s / SendBufferAmount: ${dcArr[
j
].dc.bufferedAmount()} / DataChannelOpen: ${dcArr[j].dc.isOpen()}`,
);
totalBytesSent += dcArr[j].bytesSent;
totalBytesReceived += dcArr[j].bytesReceived;
dcArr[j].bytesSent = 0;
dcArr[j].bytesReceived = 0;
}
console.log(
`Total Sent: ${byte2KB(totalBytesSent)} KB/s / Total Received: ${byte2KB(
totalBytesReceived,
)} KB/s`,
);
if (i % 5 == 0) {
console.log(
`Stats# Sent: ${byte2MB(pcMap[peerId].bytesSent())} MB / Received: ${byte2MB(
pcMap[peerId].bytesReceived(),
)} MB / rtt: ${pcMap[peerId].rtt()} ms`,
);
console.log(`Selected Candidates# ${JSON.stringify(pcMap[peerId].getSelectedCandidatePair())}`);
console.log(``);
}
i++;
}, 1000);
}
});
}
function createPeerConnection(peerId) {
// Create PeerConnection
let peerConnection = new nodeDataChannel.PeerConnection('pc', { iceServers: ['stun:stun.l.google.com:19302'] });
peerConnection.onStateChange((state) => {
console.log('State: ', state);
});
peerConnection.onGatheringStateChange((state) => {
console.log('GatheringState: ', state);
});
peerConnection.onLocalDescription((description, type) => {
ws.send(JSON.stringify({ id: peerId, type, description }));
});
peerConnection.onLocalCandidate((candidate, mid) => {
ws.send(JSON.stringify({ id: peerId, type: 'candidate', candidate, mid }));
});
peerConnection.onDataChannel((dc) => {
rl.close();
console.log('DataChannel from ' + peerId + ' received with label "', dc.getLabel() + '"');
let msgToSend = randomId(MESSAGE_SIZE);
while (!disableSend && dc.bufferedAmount() <= BUFFER_SIZE) {
dc.sendMessage(msgToSend);
}
dc.onBufferedAmountLow(() => {
while (!disableSend && dc.bufferedAmount() <= BUFFER_SIZE) {
dc.sendMessage(msgToSend);
}
});
dc.onMessage((msg) => {
// bytesReceived += msg.length;
});
});
pcMap[peerId] = peerConnection;
}
function randomId(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
function byte2KB(bytes) {
return `${Math.round(bytes / 1000)}`;
}
function byte2MB(bytes) {
return `${Math.round(bytes / (1000 * 1000))}`;
}