Skip to content

Instantly share code, notes, and snippets.

@ibc
Last active December 9, 2019 10:59

Revisions

  1. ibc revised this gist Dec 9, 2019. 1 changed file with 21 additions and 0 deletions.
    21 changes: 21 additions & 0 deletions all-tests.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    #!/usr/bin/env bash

    set -e

    ./node-sctp-mediasoup-test.js 1000

    ./node-sctp-mediasoup-test.js 1300

    ./node-sctp-mediasoup-test.js 5000

    HIGH_WATER_MARK=20000 ./node-sctp-mediasoup-test.js 20000

    NUM_MSG=2 HIGH_WATER_MARK=50000 ./node-sctp-mediasoup-test.js 50000

    PMTU=32000 NUM_MSG=3 HIGH_WATER_MARK=50000 ./node-sctp-mediasoup-test.js 50000

    HIGH_WATER_MARK=150000 TIMEOUT=2 ./node-sctp-mediasoup-test.js 150000

    # This fails. It seems (via tshark) that mediasoup does not send much bytes
    # to the node-sctp receiving Socket.
    PMTU=32000 HIGH_WATER_MARK=300000 TIMEOUT=3 ./node-sctp-mediasoup-test.js 300000
  2. ibc revised this gist Dec 9, 2019. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions node-sctp-and-mediasoup-test.js
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,7 @@ const NUM_MSG = Number(process.env.NUM_MSG) || 1;
    const PMTU = Number(process.env.PMTU);
    const RWND = Number(process.env.RWND);
    const HIGH_WATER_MARK = Number(process.env.HIGH_WATER_MARK) || 16000;
    const TIMEOUT = Number(process.env.TIMEOUT) || 2;
    const TIMEOUT = Number(process.env.TIMEOUT) || 1;
    const SRC_IP = process.env.SCR_IP || '127.0.0.1';
    const DST_IP = process.env.DST_IP || '127.0.0.1';

    @@ -37,7 +37,7 @@ function help() {
    console.log(' - PMTU : PMTU for node-sctp');
    console.log(' - RWND : RWND for node-sctp');
    console.log(' - HIGH_WATER_MARK : highWaterMark value for node-sctp Socket (default: 16000)');
    console.log(' - TIMEOUT : Time in seconds to wait for SCTP messages delivery (default: 2)');
    console.log(' - TIMEOUT : Time in seconds to wait for SCTP messages delivery (default: 1)');
    console.log(' - SRC_IP : IP of the sending mediasoup transport (default: "127.0.0.1")');
    console.log(' - DST_IP : IP of the receiving mediasoup transport (default: "127.0.0.1")');
    console.log('');
  3. ibc revised this gist Dec 9, 2019. 1 changed file with 18 additions and 13 deletions.
    31 changes: 18 additions & 13 deletions node-sctp-and-mediasoup-test.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    #!/usr/bin/env node
    #!/usr/bin/env node

    const dgram = require('dgram');
    const mediasoup = require('mediasoup');
    @@ -13,6 +13,7 @@ const MSG_SIZE = Number(process.argv[2]);
    const NUM_MSG = Number(process.env.NUM_MSG) || 1;
    const PMTU = Number(process.env.PMTU);
    const RWND = Number(process.env.RWND);
    const HIGH_WATER_MARK = Number(process.env.HIGH_WATER_MARK) || 16000;
    const TIMEOUT = Number(process.env.TIMEOUT) || 2;
    const SRC_IP = process.env.SCR_IP || '127.0.0.1';
    const DST_IP = process.env.DST_IP || '127.0.0.1';
    @@ -26,18 +27,19 @@ if (!MSG_SIZE) {

    function help() {
    console.log('');
    console.log('USAGE: [NUM_MSG=X] [PMTU=X] [RWND=X] [TIMEOUT=X] [SRC_IP=X] [DST_IP=X] ./node-sctp-mediasoup-test.js MSG_SIZE');
    console.log('USAGE: [NUM_MSG=X] [PMTU=X] [RWND=X] [HIGH_WATER_MARK=X] [TIMEOUT=X] [SRC_IP=X] [DST_IP=X] ./node-sctp-mediasoup-test.js MSG_SIZE');
    console.log('');
    console.log(' Command line arguments:');
    console.log(' - MSG_SIZE : Size in bytes of the SCTP message to be sent (mandatory)');
    console.log('');
    console.log(' Environment variables:');
    console.log(' - NUM_MSG : Number of messages to send all together (default: 1)');
    console.log(' - PMTU : PMTU for node-sctp');
    console.log(' - RWND : RWND for node-sctp');
    console.log(' - TIMEOUT : Time in seconds to wait for SCTP messages delivery (default: 2)');
    console.log(' - SRC_IP : IP of the sending mediasoup transport (default: "127.0.0.1")');
    console.log(' - DST_IP : IP of the receiving mediasoup transport (default: "127.0.0.1")');
    console.log(' - NUM_MSG : Number of messages to send all together (default: 1)');
    console.log(' - PMTU : PMTU for node-sctp');
    console.log(' - RWND : RWND for node-sctp');
    console.log(' - HIGH_WATER_MARK : highWaterMark value for node-sctp Socket (default: 16000)');
    console.log(' - TIMEOUT : Time in seconds to wait for SCTP messages delivery (default: 2)');
    console.log(' - SRC_IP : IP of the sending mediasoup transport (default: "127.0.0.1")');
    console.log(' - DST_IP : IP of the receiving mediasoup transport (default: "127.0.0.1")');
    console.log('');
    }

    @@ -56,11 +58,12 @@ run()
    async function run() {
    console.log();
    console.log(
    '[INFO] running test with MSG_SIZE:%s, NUM_MSG:%d, PMTU:%s, RWND:%s, TIMEOUT:%d, SRC_IP:%s, DST_IP:%s',
    '[INFO] running test with MSG_SIZE:%s, NUM_MSG:%d, PMTU:%s, RWND:%s, HIGH_WATER_MARK:%s, TIMEOUT:%d, SRC_IP:%s, DST_IP:%s',
    MSG_SIZE,
    NUM_MSG,
    PMTU,
    RWND,
    HIGH_WATER_MARK,
    TIMEOUT,
    SRC_IP,
    DST_IP);
    @@ -71,10 +74,10 @@ async function run() {
    let inboundSctpStreamTotalReceivedMessages = 0;

    // Set node-sctp global defaults.
    if (PMTU)
    sctp.defaults({ PMTU: PMTU });
    if (RWND)
    sctp.defaults({ RWND: RWND });
    // if (PMTU)
    // sctp.defaults({ PMTU: PMTU });
    // if (RWND)
    // sctp.defaults({ RWND: RWND });

    // Create a mediasoup Worker.
    const worker = await mediasoup.createWorker({
    @@ -116,6 +119,7 @@ async function run() {
    address: sendTransport.tuple.localIp,
    port: sendTransport.tuple.localPort,
    },
    highWaterMark: HIGH_WATER_MARK
    });

    sendSctpSocket.on('error', (error) => {
    @@ -182,6 +186,7 @@ async function run() {
    address: recvTransport.tuple.localIp,
    port: recvTransport.tuple.localPort,
    },
    highWaterMark: HIGH_WATER_MARK
    });

    recvSctpSocket.on('error', (error) => {
  4. ibc revised this gist Dec 8, 2019. 1 changed file with 12 additions and 6 deletions.
    18 changes: 12 additions & 6 deletions node-sctp-and-mediasoup-test.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    #!/usr/bin/env node
    #!/usr/bin/env node

    const dgram = require('dgram');
    const mediasoup = require('mediasoup');
    @@ -11,7 +11,8 @@ if ([ '--help', '-h' ].includes(process.argv[2])) {

    const MSG_SIZE = Number(process.argv[2]);
    const NUM_MSG = Number(process.env.NUM_MSG) || 1;
    const PMTU = Number(process.env.PMTU) || 1500;
    const PMTU = Number(process.env.PMTU);
    const RWND = Number(process.env.RWND);
    const TIMEOUT = Number(process.env.TIMEOUT) || 2;
    const SRC_IP = process.env.SCR_IP || '127.0.0.1';
    const DST_IP = process.env.DST_IP || '127.0.0.1';
    @@ -25,14 +26,15 @@ if (!MSG_SIZE) {

    function help() {
    console.log('');
    console.log('USAGE: [NUM_MSG=X] [PMTU=X] [TIMEOUT=X] [SRC_IP=X] [DST_IP=X] ./node-sctp-mediasoup-test.js MSG_SIZE');
    console.log('USAGE: [NUM_MSG=X] [PMTU=X] [RWND=X] [TIMEOUT=X] [SRC_IP=X] [DST_IP=X] ./node-sctp-mediasoup-test.js MSG_SIZE');
    console.log('');
    console.log(' Command line arguments:');
    console.log(' - MSG_SIZE : Size in bytes of the SCTP message to be sent (mandatory)');
    console.log('');
    console.log(' Environment variables:');
    console.log(' - NUM_MSG : Number of messages to send all together (default: 1)');
    console.log(' - PMTU : PMTU for node-sctp');
    console.log(' - RWND : RWND for node-sctp');
    console.log(' - TIMEOUT : Time in seconds to wait for SCTP messages delivery (default: 2)');
    console.log(' - SRC_IP : IP of the sending mediasoup transport (default: "127.0.0.1")');
    console.log(' - DST_IP : IP of the receiving mediasoup transport (default: "127.0.0.1")');
    @@ -54,10 +56,11 @@ run()
    async function run() {
    console.log();
    console.log(
    '[INFO] running test with MSG_SIZE:%s, NUM_MSG:%d, PMTU:%s, TIMEOUT:%d, SRC_IP:%s, DST_IP:%s',
    '[INFO] running test with MSG_SIZE:%s, NUM_MSG:%d, PMTU:%s, RWND:%s, TIMEOUT:%d, SRC_IP:%s, DST_IP:%s',
    MSG_SIZE,
    NUM_MSG,
    PMTU,
    RWND,
    TIMEOUT,
    SRC_IP,
    DST_IP);
    @@ -67,8 +70,11 @@ async function run() {
    let inboundSctpStreamTotalReceivedBytes = 0;
    let inboundSctpStreamTotalReceivedMessages = 0;

    // Set PMTU for SCTP.
    sctp.defaults({ PMTU: PMTU });
    // Set node-sctp global defaults.
    if (PMTU)
    sctp.defaults({ PMTU: PMTU });
    if (RWND)
    sctp.defaults({ RWND: RWND });

    // Create a mediasoup Worker.
    const worker = await mediasoup.createWorker({
  5. ibc revised this gist Dec 5, 2019. 1 changed file with 61 additions and 43 deletions.
    104 changes: 61 additions & 43 deletions node-sctp-and-mediasoup-test.js
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    #!/usr/bin/env node

    const dgram = require('dgram');
    const mediasoup = require('mediasoup');
    const sctp = require('sctp');
    @@ -7,31 +9,33 @@ if ([ '--help', '-h' ].includes(process.argv[2])) {
    process.exit(0);
    }

    const SIZE = Number(process.argv[2]);
    const PMTU = Number(process.argv[3]);
    const COUNT = Number(process.argv[4]) || 1;
    const TIMEOUT = Number(process.argv[5]) || 2;

    if (!SIZE) {
    console.error('[ERROR]: missing SIZE comamnd line argument');
    const MSG_SIZE = Number(process.argv[2]);
    const NUM_MSG = Number(process.env.NUM_MSG) || 1;
    const PMTU = Number(process.env.PMTU) || 1500;
    const TIMEOUT = Number(process.env.TIMEOUT) || 2;
    const SRC_IP = process.env.SCR_IP || '127.0.0.1';
    const DST_IP = process.env.DST_IP || '127.0.0.1';

    help();
    process.exit(1);
    } else if (!PMTU) {
    console.error('[ERROR]: missing PMTU comamnd line argument');
    if (!MSG_SIZE) {
    console.error('[ERROR]: missing MSG_SIZE command line argument');

    help();
    process.exit(1);
    }

    function help() {
    console.log('');
    console.log('USAGE: node node-sctp-and-mediasoup-test.js SIZE PMTU [COUNT] [TIMEOUT]');
    console.log('USAGE: [NUM_MSG=X] [PMTU=X] [TIMEOUT=X] [SRC_IP=X] [DST_IP=X] ./node-sctp-mediasoup-test.js MSG_SIZE');
    console.log('');
    console.log(' SIZE : Size in bytes of the message to send');
    console.log(' PMTU : PMTU for node-sctp');
    console.log(' COUNT : Number of messages to send all together (default: 1)');
    console.log(' TIMEOUT : Time in seconds to wait for SCTP messages delivery (default: 2)');
    console.log(' Command line arguments:');
    console.log(' - MSG_SIZE : Size in bytes of the SCTP message to be sent (mandatory)');
    console.log('');
    console.log(' Environment variables:');
    console.log(' - NUM_MSG : Number of messages to send all together (default: 1)');
    console.log(' - PMTU : PMTU for node-sctp');
    console.log(' - TIMEOUT : Time in seconds to wait for SCTP messages delivery (default: 2)');
    console.log(' - SRC_IP : IP of the sending mediasoup transport (default: "127.0.0.1")');
    console.log(' - DST_IP : IP of the receiving mediasoup transport (default: "127.0.0.1")');
    console.log('');
    }

    @@ -48,9 +52,15 @@ run()
    });

    async function run() {
    console.log();
    console.log(
    '[INFO] running test with SIZE:%s, PMTU:%s, COUNT:%d, TIMEOUT:%d...',
    SIZE, PMTU, COUNT, TIMEOUT);
    '[INFO] running test with MSG_SIZE:%s, NUM_MSG:%d, PMTU:%s, TIMEOUT:%d, SRC_IP:%s, DST_IP:%s',
    MSG_SIZE,
    NUM_MSG,
    PMTU,
    TIMEOUT,
    SRC_IP,
    DST_IP);

    let outboundSctpStreamTotalSentBytes = 0;
    let outboundSctpStreamTotalSentMessages = 0;
    @@ -72,7 +82,7 @@ async function run() {
    // Create a mediasoup PlainRtpTransport for connecting the sending node-sctp
    // Socket.
    const sendTransport = await router.createPlainRtpTransport({
    listenIp: { ip: '127.0.0.1' },
    listenIp: { ip: SRC_IP },
    enableSctp: true,
    numSctpStreams: { OS: 512, MIS: 512 },
    maxSctpMessageSize: 4000000 // 4 MB
    @@ -81,12 +91,12 @@ async function run() {
    // Node UDP socket for the sending SCTP.
    const sendUdpSocket = dgram.createSocket({ type: 'udp4' });

    await new Promise(resolve => sendUdpSocket.bind(11111, '127.0.0.1', resolve));
    await new Promise(resolve => sendUdpSocket.bind(11111, SRC_IP, resolve));

    const localSendUdpPort = sendUdpSocket.address().port;

    // Connect the mediasoup send PlainRtpTransport to the UDP socket.
    await sendTransport.connect({ ip: '127.0.0.1', port: localSendUdpPort });
    await sendTransport.connect({ ip: SRC_IP, port: localSendUdpPort });

    // Create a node-sctp sending Socket.
    const sendSctpSocket = sctp.connect({
    @@ -108,7 +118,7 @@ async function run() {
    process.exit(2);
    });

    console.log('[INFO] waiting for the sending SCTP assoiation to be open...');
    console.log('[INFO] waiting for the sending SCTP association to be open');

    // Wait for the SCTP association to be open.
    await Promise.race([
    @@ -118,16 +128,16 @@ async function run() {
    new Promise(resolve => sendSctpSocket.on('connect', resolve)),
    ]);

    console.log('[INFO] creating a node-sctp outbound Stream [streamId:1]...');
    console.log('[INFO] creating a node-sctp outbound Stream [streamId:1]');

    // Create a node-sctp outbound Stream with id 1 (don't use the implicit Stream in the
    // node-sctp Socket).
    const outboundSctpStream = sendSctpSocket.createStream(1);

    // Create a mediasoup DataProducer representing the node-sctp outbound Stream.
    console.log(
    '[INFO] creating a mediasoup DataProducer associated to the SCTP stream...');
    '[INFO] creating a mediasoup DataProducer associated to the node-sctp outbound Stream');

    // Create a mediasoup DataProducer representing the node-sctp outbound Stream.
    const dataProducer = await sendTransport.produceData({
    sctpStreamParameters: {
    streamId: 1,
    @@ -138,7 +148,7 @@ async function run() {
    // Create a mediasoup PlainRtpTransport for consuming SCTP data from the
    // node-sctp outbound Stream.
    const recvTransport = await router.createPlainRtpTransport({
    listenIp: { ip: '127.0.0.1' },
    listenIp: { ip: DST_IP },
    enableSctp: true,
    numSctpStreams: { OS: 512, MIS: 512 },
    maxSctpMessageSize: 4000000 // 4 MB
    @@ -147,12 +157,12 @@ async function run() {
    // Node UDP socket for the receiving SCTP.
    const recvUdpSocket = dgram.createSocket({ type: 'udp4' });

    await new Promise(resolve => recvUdpSocket.bind(22222, '127.0.0.1', resolve));
    await new Promise(resolve => recvUdpSocket.bind(22222, DST_IP, resolve));

    const localRecvUdpPort = recvUdpSocket.address().port;

    // Connect the mediasoup receiving PlainRtpTransport to the UDP socket.
    await recvTransport.connect({ ip: '127.0.0.1', port: localRecvUdpPort });
    await recvTransport.connect({ ip: DST_IP, port: localRecvUdpPort });

    // Create a node-sctp receiving Socket.
    const recvSctpSocket = sctp.connect({
    @@ -174,7 +184,7 @@ async function run() {
    process.exit(2);
    });

    console.log('[INFO] waiting for the receiving SCTP assoiation to be open...');
    console.log('[INFO] waiting for the receiving SCTP association to be open');

    await Promise.race([
    new Promise((resolve, reject) => {
    @@ -183,19 +193,25 @@ async function run() {
    new Promise(resolve => recvSctpSocket.on('connect', resolve)),
    ]);

    console.log('[INFO] creating a mediasoup DataConsumer...');
    let inboundSctpStreamCreated = false;

    // Handle SCTP messages received in the node-sctp receiving Socket.
    recvSctpSocket.on('stream', (stream, streamId) => {
    recvSctpSocket.on('stream', async (stream, streamId) => {
    console.log(
    '[INFO] node-sctp inbound Stream created in the node-sctp receiving Socket [streamId:%d]',
    streamId);

    stream.on('data', (data) => {
    const ppid = data.ppid;
    if (inboundSctpStreamCreated) {
    console.error('[ERROR] just a single node-sctp inbound Stream should be generated');

    process.exit(2);
    }

    inboundSctpStreamCreated = true;

    stream.on('data', (buffer) => {
    // Ensure it's a WebRTC DataChannel string.
    if (ppid !== sctp.PPID.WEBRTC_STRING) {
    if (buffer.ppid !== sctp.PPID.WEBRTC_STRING) {
    console.error(
    '[ERROR] non WebRTC string data received in the node-sctp inbound Stream');

    @@ -204,23 +220,25 @@ async function run() {

    console.log(
    '[INFO] SCTP message received in the node-sctp inbound Stream [size:%d]',
    data.byteLength);
    buffer.byteLength);

    inboundSctpStreamTotalReceivedBytes += data.byteLength;
    inboundSctpStreamTotalReceivedBytes += buffer.byteLength;
    inboundSctpStreamTotalReceivedMessages++;
    });
    });

    // Create a mediasoup DataConsumer to deliver SCTP messages to the node-sctp
    // inbound Stream.
    const dataConsumer = await recvTransport.consumeData({
    // Create a mediasoup DataConsumer representing the node-sctp inbound Stream.
    console.log(
    '[INFO] creating a mediasoup DataConsumer associated to the node-sctp inbound Stream');

    dataConsumer = await recvTransport.consumeData({
    dataProducerId: dataProducer.id
    });

    for (let i = 0; i < COUNT; ++i) {
    sendData(outboundSctpStream, dataProducer, SIZE);
    for (let i = 0; i < NUM_MSG; ++i) {
    sendData(outboundSctpStream, dataProducer, MSG_SIZE);

    outboundSctpStreamTotalSentBytes += SIZE;
    outboundSctpStreamTotalSentBytes += MSG_SIZE;
    outboundSctpStreamTotalSentMessages++;
    }

    @@ -269,7 +287,7 @@ async function run() {

    function sendData(outboundSctpStream, dataProducer, size) {
    console.log(
    '[INFO] sending a message of %d bytes from the node-sctp outbound Stream...',
    '[INFO] sending a message of %d bytes from the node-sctp outbound Stream',
    size);

    const buffer = Buffer.alloc(size, 'X');
  6. ibc revised this gist Dec 5, 2019. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions node-sctp-and-mediasoup-test.js
    Original file line number Diff line number Diff line change
    @@ -28,10 +28,10 @@ function help() {
    console.log('');
    console.log('USAGE: node node-sctp-and-mediasoup-test.js SIZE PMTU [COUNT] [TIMEOUT]');
    console.log('');
    console.log(' - SIZE : Size in bytes of the message to send');
    console.log(' - PMTU : PMTU for node-sctp');
    console.log(' - COUNT : Number of messages to send all together (default: 1)');
    console.log(' - TIMEOUT : Time in seconds to wait for mediasoup DataProducer to receive all sent bytes (default: 2)');
    console.log(' SIZE : Size in bytes of the message to send');
    console.log(' PMTU : PMTU for node-sctp');
    console.log(' COUNT : Number of messages to send all together (default: 1)');
    console.log(' TIMEOUT : Time in seconds to wait for SCTP messages delivery (default: 2)');
    console.log('');
    }

  7. ibc revised this gist Dec 5, 2019. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions node-sctp-and-mediasoup-test.js
    Original file line number Diff line number Diff line change
    @@ -75,7 +75,7 @@ async function run() {
    listenIp: { ip: '127.0.0.1' },
    enableSctp: true,
    numSctpStreams: { OS: 512, MIS: 512 },
    maxSctpMessageSize: 1000000 // 1 Mbit
    maxSctpMessageSize: 4000000 // 4 MB
    });

    // Node UDP socket for the sending SCTP.
    @@ -141,7 +141,7 @@ async function run() {
    listenIp: { ip: '127.0.0.1' },
    enableSctp: true,
    numSctpStreams: { OS: 512, MIS: 512 },
    maxSctpMessageSize: 1000000 // 1 Mbit
    maxSctpMessageSize: 4000000 // 4 MB
    });

    // Node UDP socket for the receiving SCTP.
  8. ibc created this gist Dec 5, 2019.
    281 changes: 281 additions & 0 deletions node-sctp-and-mediasoup-test.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,281 @@
    const dgram = require('dgram');
    const mediasoup = require('mediasoup');
    const sctp = require('sctp');

    if ([ '--help', '-h' ].includes(process.argv[2])) {
    help();
    process.exit(0);
    }

    const SIZE = Number(process.argv[2]);
    const PMTU = Number(process.argv[3]);
    const COUNT = Number(process.argv[4]) || 1;
    const TIMEOUT = Number(process.argv[5]) || 2;

    if (!SIZE) {
    console.error('[ERROR]: missing SIZE comamnd line argument');

    help();
    process.exit(1);
    } else if (!PMTU) {
    console.error('[ERROR]: missing PMTU comamnd line argument');

    help();
    process.exit(1);
    }

    function help() {
    console.log('');
    console.log('USAGE: node node-sctp-and-mediasoup-test.js SIZE PMTU [COUNT] [TIMEOUT]');
    console.log('');
    console.log(' - SIZE : Size in bytes of the message to send');
    console.log(' - PMTU : PMTU for node-sctp');
    console.log(' - COUNT : Number of messages to send all together (default: 1)');
    console.log(' - TIMEOUT : Time in seconds to wait for mediasoup DataProducer to receive all sent bytes (default: 2)');
    console.log('');
    }

    run()
    .then(() => {
    console.log('[INFO] test succeeds :)');

    process.exit(0);
    })
    .catch((error) => {
    console.error('[ERROR]: test failed: %o', error);

    process.exit(1);
    });

    async function run() {
    console.log(
    '[INFO] running test with SIZE:%s, PMTU:%s, COUNT:%d, TIMEOUT:%d...',
    SIZE, PMTU, COUNT, TIMEOUT);

    let outboundSctpStreamTotalSentBytes = 0;
    let outboundSctpStreamTotalSentMessages = 0;
    let inboundSctpStreamTotalReceivedBytes = 0;
    let inboundSctpStreamTotalReceivedMessages = 0;

    // Set PMTU for SCTP.
    sctp.defaults({ PMTU: PMTU });

    // Create a mediasoup Worker.
    const worker = await mediasoup.createWorker({
    logLevel: 'debug',
    logTags: [ 'sctp' ]
    });

    // Create a mediasoup Router.
    const router = await worker.createRouter();

    // Create a mediasoup PlainRtpTransport for connecting the sending node-sctp
    // Socket.
    const sendTransport = await router.createPlainRtpTransport({
    listenIp: { ip: '127.0.0.1' },
    enableSctp: true,
    numSctpStreams: { OS: 512, MIS: 512 },
    maxSctpMessageSize: 1000000 // 1 Mbit
    });

    // Node UDP socket for the sending SCTP.
    const sendUdpSocket = dgram.createSocket({ type: 'udp4' });

    await new Promise(resolve => sendUdpSocket.bind(11111, '127.0.0.1', resolve));

    const localSendUdpPort = sendUdpSocket.address().port;

    // Connect the mediasoup send PlainRtpTransport to the UDP socket.
    await sendTransport.connect({ ip: '127.0.0.1', port: localSendUdpPort });

    // Create a node-sctp sending Socket.
    const sendSctpSocket = sctp.connect({
    localPort: 5000, // Required for SCTP over plain UDP in mediasoup.
    port: 5000, // Required for SCTP over plain UDP in mediasoup.
    OS: sendTransport.sctpParameters.OS,
    MIS: sendTransport.sctpParameters.MIS,
    unordered: false,
    udpTransport: sendUdpSocket,
    udpPeer: {
    address: sendTransport.tuple.localIp,
    port: sendTransport.tuple.localPort,
    },
    });

    sendSctpSocket.on('error', (error) => {
    console.error('[ERROR] node-sctp sending Socket "error" event: %o', error);

    process.exit(2);
    });

    console.log('[INFO] waiting for the sending SCTP assoiation to be open...');

    // Wait for the SCTP association to be open.
    await Promise.race([
    new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('SCTP connection timeout')), 2000)
    }),
    new Promise(resolve => sendSctpSocket.on('connect', resolve)),
    ]);

    console.log('[INFO] creating a node-sctp outbound Stream [streamId:1]...');

    // Create a node-sctp outbound Stream with id 1 (don't use the implicit Stream in the
    // node-sctp Socket).
    const outboundSctpStream = sendSctpSocket.createStream(1);

    console.log(
    '[INFO] creating a mediasoup DataProducer associated to the SCTP stream...');

    // Create a mediasoup DataProducer representing the node-sctp outbound Stream.
    const dataProducer = await sendTransport.produceData({
    sctpStreamParameters: {
    streamId: 1,
    ordered: true,
    }
    });

    // Create a mediasoup PlainRtpTransport for consuming SCTP data from the
    // node-sctp outbound Stream.
    const recvTransport = await router.createPlainRtpTransport({
    listenIp: { ip: '127.0.0.1' },
    enableSctp: true,
    numSctpStreams: { OS: 512, MIS: 512 },
    maxSctpMessageSize: 1000000 // 1 Mbit
    });

    // Node UDP socket for the receiving SCTP.
    const recvUdpSocket = dgram.createSocket({ type: 'udp4' });

    await new Promise(resolve => recvUdpSocket.bind(22222, '127.0.0.1', resolve));

    const localRecvUdpPort = recvUdpSocket.address().port;

    // Connect the mediasoup receiving PlainRtpTransport to the UDP socket.
    await recvTransport.connect({ ip: '127.0.0.1', port: localRecvUdpPort });

    // Create a node-sctp receiving Socket.
    const recvSctpSocket = sctp.connect({
    localPort: 5000, // Required for SCTP over plain UDP in mediasoup.
    port: 5000, // Required for SCTP over plain UDP in mediasoup.
    OS: recvTransport.sctpParameters.OS,
    MIS: recvTransport.sctpParameters.MIS,
    unordered: false,
    udpTransport: recvUdpSocket,
    udpPeer: {
    address: recvTransport.tuple.localIp,
    port: recvTransport.tuple.localPort,
    },
    });

    recvSctpSocket.on('error', (error) => {
    console.error('[ERROR] node-sctp receiving Socket "error" event: %o', error);

    process.exit(2);
    });

    console.log('[INFO] waiting for the receiving SCTP assoiation to be open...');

    await Promise.race([
    new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('SCTP connection timeout')), 2000)
    }),
    new Promise(resolve => recvSctpSocket.on('connect', resolve)),
    ]);

    console.log('[INFO] creating a mediasoup DataConsumer...');

    // Handle SCTP messages received in the node-sctp receiving Socket.
    recvSctpSocket.on('stream', (stream, streamId) => {
    console.log(
    '[INFO] node-sctp inbound Stream created in the node-sctp receiving Socket [streamId:%d]',
    streamId);

    stream.on('data', (data) => {
    const ppid = data.ppid;

    // Ensure it's a WebRTC DataChannel string.
    if (ppid !== sctp.PPID.WEBRTC_STRING) {
    console.error(
    '[ERROR] non WebRTC string data received in the node-sctp inbound Stream');

    process.exit(2);
    }

    console.log(
    '[INFO] SCTP message received in the node-sctp inbound Stream [size:%d]',
    data.byteLength);

    inboundSctpStreamTotalReceivedBytes += data.byteLength;
    inboundSctpStreamTotalReceivedMessages++;
    });
    });

    // Create a mediasoup DataConsumer to deliver SCTP messages to the node-sctp
    // inbound Stream.
    const dataConsumer = await recvTransport.consumeData({
    dataProducerId: dataProducer.id
    });

    for (let i = 0; i < COUNT; ++i) {
    sendData(outboundSctpStream, dataProducer, SIZE);

    outboundSctpStreamTotalSentBytes += SIZE;
    outboundSctpStreamTotalSentMessages++;
    }

    // Wait a bit for all bytes to be delivered.
    await new Promise((resolve) => setTimeout(resolve, TIMEOUT * 1000));

    // Check sent and received bytes and SCTP messages.
    const dataProducerStats = await dataProducer.getStats();
    const dataProducerTotalReceivedBytes = dataProducerStats[0].bytesReceived;
    const dataProducerTotalReceivedMessages = dataProducerStats[0].messagesReceived;
    const dataConsumerStats = await dataConsumer.getStats();
    const dataConsumerTotalSentBytes = dataConsumerStats[0].bytesSent;
    const dataConsumerTotalSentMessages = dataConsumerStats[0].messagesSent;

    console.log();
    console.log('[INFO] test results:');
    console.log(
    '- node-sctp outbound Stream sent %d bytes in %d SCTP messages to mediasoup DataProducer',
    outboundSctpStreamTotalSentBytes,
    outboundSctpStreamTotalSentMessages);
    console.log(
    '- mediasoup DataProducer received %d bytes in %d SCTP messages from node-sctp outbound Stream',
    dataProducerTotalReceivedBytes,
    dataProducerTotalReceivedMessages);
    console.log(
    '- mediasoup DataConsumer sent %d bytes in %d SCTP messages to node-sctp inbound Stream',
    dataConsumerTotalSentBytes,
    dataConsumerTotalSentMessages);
    console.log(
    '- node-sctp inbound Streamm received %d bytes in %d SCTP messages from mediasoup DataConsumer',
    inboundSctpStreamTotalReceivedBytes,
    inboundSctpStreamTotalReceivedMessages);
    console.log();

    if (
    outboundSctpStreamTotalSentBytes !== dataProducerTotalReceivedBytes ||
    dataProducerTotalReceivedBytes !== dataConsumerTotalSentBytes ||
    dataConsumerTotalSentBytes !== inboundSctpStreamTotalReceivedBytes ||
    outboundSctpStreamTotalSentMessages !== dataProducerTotalReceivedMessages ||
    dataProducerTotalReceivedMessages !== dataConsumerTotalSentMessages ||
    dataConsumerTotalSentMessages !== inboundSctpStreamTotalReceivedMessages
    ) {
    throw new Error('SCTP sent and received bytes and/or messages do not match!');
    }
    }

    function sendData(outboundSctpStream, dataProducer, size) {
    console.log(
    '[INFO] sending a message of %d bytes from the node-sctp outbound Stream...',
    size);

    const buffer = Buffer.alloc(size, 'X');

    // Set ppid of type WebRTC DataChannel string.
    buffer.ppid = sctp.PPID.WEBRTC_STRING;

    outboundSctpStream.write(buffer);
    }