Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error [ERR_MULTIPLE_CALLBACK]: Callback called multiple times #15

Open
ibc opened this issue Dec 3, 2019 · 7 comments
Open

Error [ERR_MULTIPLE_CALLBACK]: Callback called multiple times #15

ibc opened this issue Dec 3, 2019 · 7 comments
Assignees
Labels

Comments

@ibc
Copy link
Contributor

ibc commented Dec 3, 2019

I'm getting the following fatal error. I don't know yet when/why it happens (on it). However, it's a fatal error because it's an internal unhandled "error" event, so it terminates the Node process.

Error [ERR_MULTIPLE_CALLBACK]: Callback called multiple times
    at onwrite (_stream_writable.js:465:11)
    at /Users/ibc/xxxxxxx/node_modules/sctp/lib/sockets.js:134:9
    at /Users/ibc/xxxxxxx/node_modules/sctp/lib/sockets.js:174:11
    at /Users/ibc/xxxxxxx/node_modules/sctp/lib/association.js:1658:11
    at /Users/ibc/xxxxxxx/node_modules/sctp/lib/association.js:1335:13
    at Array.forEach (<anonymous>)
    at /Users/ibc/xxxxxxx/node_modules/sctp/lib/association.js:1332:19
    at processTicksAndRejections (internal/process/task_queues.js:80:21)

Note that I'm setting an "error" listener in the node-sctp Socket instance, so the problem is that such a "error" exception is not being caught internally.

@ibc
Copy link
Contributor Author

ibc commented Dec 3, 2019

The data being sent via the SCTP stream is as follows:

data = '{"event":"bomberman-start-game","payload":{"id":"ibc@mycompany","mapName":"cold_map","layerInfo":{"name":"Blocks","width":28,"height":18,"x":0,"y":0,"data":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,3,3,3,3,2,1,2,3,2,3,2,3,2,3,2,3,2,3,1,2,3,3,3,2,3,1,1,3,2,1,1,3,2,2,3,2,3,2,3,2,3,2,3,2,3,2,1,3,3,2,2,2,3,1,1,2,3,3,3,3,3,2,3,1,1,1,1,3,1,1,1,1,2,3,2,3,3,1,1,1,2,1,1,1,1,1,2,2,2,2,3,3,3,2,3,3,3,3,3,2,3,3,1,2,3,3,3,3,3,1,1,3,3,3,2,3,3,3,3,2,2,3,3,2,1,1,3,3,2,3,1,2,3,3,2,3,2,1,1,2,2,3,1,1,1,1,2,2,1,1,1,1,1,1,2,2,1,1,3,2,1,1,1,2,1,1,1,2,2,3,2,3,3,1,3,3,1,1,3,3,2,2,3,3,2,2,3,3,2,1,3,3,3,1,1,1,1,1,2,3,3,1,3,2,3,3,2,3,3,3,3,3,3,3,3,3,1,1,3,2,2,1,1,3,3,3,3,3,3,1,3,2,2,2,2,3,1,1,1,1,1,2,3,3,1,2,3,2,1,1,1,3,2,1,1,2,3,1,3,3,3,3,3,3,1,3,3,3,3,2,3,2,3,2,3,2,2,1,1,1,2,2,2,2,3,1,1,1,2,2,2,1,1,2,3,2,3,2,3,2,3,2,2,3,3,1,1,3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,2,3,2,1,1,1,1,1,2,3,3,1,1,1,1,2,2,1,1,2,2,2,3,3,2,2,2,3,3,3,3,2,3,2,3,1,1,1,1,1,1,2,3,3,3,3,3,3,3,1,2,3,1,1,1,1,2,2,3,1,2,2,3,2,2,2,2,1,1,3,2,2,1,2,3,2,3,1,3,2,3,2,3,2,3,2,3,2,3,1,1,3,3,3,3,1,1,3,3,2,3,2,1,2,3,1,3,3,2,3,2,3,2,3,3,1,3,3,3,3,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"opacity":1,"type":"tilelayer","visible":true,"properties":{"max_players":16,"collisionTiles":[1,2],"empty":3,"balk":2,"wall":1,"spawns":[{"col":6,"row":3},{"col":8,"row":14},{"col":20,"row":15},{"col":22,"row":1},{"col":5,"row":5},{"col":3,"row":12},{"col":26,"row":7},{"col":19,"row":4},{"col":13,"row":4},{"col":16,"row":13},{"col":18,"row":10},{"col":2,"row":5},{"col":8,"row":8},{"col":20,"row":8},{"col":5,"row":9},{"col":25,"row":11}]}},"players":{"ijqwgukkizuKNHS5AAAA":{"id":"ijqwgukkizuKNHS5AAAA","spawnGroup":0,"spawn":{"x":192,"y":96},"position":{"x":192,"y":96},"spawnOnGrid":{"col":6,"row":3},"name":"Iñaki","isAlive":true,"power":1}},"playerSpawnsGroups":[[{"col":8,"row":14},{"col":20,"row":15},{"col":22,"row":1}],[{"col":5,"row":5},{"col":3,"row":12},{"col":26,"row":7},{"col":19,"row":4}],[{"col":13,"row":4},{"col":16,"row":13},{"col":18,"row":10},{"col":2,"row":5}],[{"col":8,"row":8},{"col":20,"row":8},{"col":5,"row":9},{"col":25,"row":11}]],"shadowMap":[[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,2,0,0,0,0,2,1,2,0,2,0,2,0,2,0,2,0,2,0,1,2,0,0,0,2,0,1],[1,0,2,1,1,0,2,2,0,2,0,2,0,2,0,2,0,2,0,2,1,0,0,2,2,2,0,1],[1,2,0,0,0,0,0,2,0,1,1,1,1,0,1,1,1,1,2,0,2,0,0,1,1,1,2,1],[1,1,1,1,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,1,2,0,0,0,0,0,1],[1,0,0,0,2,0,0,0,0,2,2,0,0,2,1,1,0,0,2,0,1,2,0,0,2,0,2,1],[1,2,2,0,1,1,1,1,2,2,1,1,1,1,1,1,2,2,1,1,0,2,1,1,1,2,1,1],[1,2,2,0,2,0,0,1,0,0,1,1,0,0,2,2,0,0,2,2,0,0,2,1,0,0,0,1],[1,1,1,1,2,0,0,1,0,2,0,0,2,0,0,0,0,0,0,0,0,0,1,1,0,2,2,1],[1,0,0,0,0,0,0,1,0,2,2,2,2,0,1,1,1,1,1,2,0,0,1,2,0,2,1,1],[1,0,2,1,1,2,0,1,0,0,0,0,0,0,1,0,0,0,0,2,0,2,0,2,0,2,2,1],[1,1,2,2,2,2,0,1,1,1,2,2,2,1,1,2,0,2,0,2,0,2,0,2,2,0,0,1],[1,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,2,0,2,1,1,1,1,1,2,0,0,1],[1,1,1,2,2,1,1,2,2,2,0,0,2,2,2,0,0,0,0,2,0,2,0,1,1,1,1,1],[1,2,0,0,0,0,0,0,0,1,2,0,1,1,1,1,2,2,0,1,2,2,0,2,2,2,2,1],[1,0,2,2,1,2,0,2,0,1,0,2,0,2,0,2,0,2,0,2,0,1,1,0,0,0,0,1],[1,0,0,2,0,2,1,2,0,1,0,0,2,0,2,0,2,0,0,1,0,0,0,0,2,2,2,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]],"spoils":{},"bombs":{}}}'

...which is 3214 bytes long.

And I'm using it as follows:

const sctp = require('sctp');
const dgram = require('dgram');

// Set node-sctp default PMTU to 1200. <----- UPPS ?
sctp.defaults({ PMTU: 1200 });

const udpSocket = dgram.createSocket({ type: 'udp4' });

// Connect the udpSocket and so on
// [...]

const sctpSocket = sctp.connect({
      localPort: 5000, // Required for SCTP over plain UDP in mediasoup.
      port: 5000, // Required for SCTP over plain UDP in mediasoup.
      OS: 128,
      MIS: 128,
      unordered: false,
      udpTransport: udpSocket, // previously created
      udpPeer: {
        address: remoteUdpIp,
        port: remoteUdpPort,
      },
    });

// Wait for the SCTP socket to be connected (SCTP association done)
// [...]

// Send data above
const buffer = Buffer.from(JSON.stringify(data));

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

sctpStream.write(buffer);

BTW super confirmed that the error happens due the size of data (3214 bytes). It does not happen with smaller data. It also happens if I remove the sctp.defaults({ PMTU: 1200 }); line.

@latysheff
Copy link
Owner

Thanks for the snippet. I'll look into it.

@ibc
Copy link
Contributor Author

ibc commented Dec 3, 2019

NOTE: in order to install mediasoup you need this common requirements in your host. And then: npm install mediasoup@3

@latysheff latysheff added the bug label Dec 3, 2019
@latysheff latysheff self-assigned this Dec 3, 2019
@latysheff
Copy link
Owner

If one need to control message boundaries (so that they have same SSN), we again need to hijack Buffer object, though it can be done internally, I hope.
Currently, socket API splits chunks at about RWND size before feeding into SCTP API.
This was done quite ugly, and must be rewritten.

Proper buffering with highWaterMark should be done in sockets, and SCTP API should allow arbitrary buffer size in SEND()

@ibc
Copy link
Contributor Author

ibc commented Dec 4, 2019

Currently, socket API splits chunks at about RWND size before feeding into SCTP API.
This was done quite ugly, and must be rewritten.

Yep, a message given to write() must not be splitted into N SCTP messages because the remote application is supposed to receive a single "message" rather than 2 messages (that probably will be invalid).

@ibc
Copy link
Contributor Author

ibc commented Dec 5, 2019

Well, I've written a gist with a more complete test script and failing examples:

https://gist.github.com/ibc/17ed95cd2f08b84222c8820b034cd391

@latysheff
Copy link
Owner

Made comment on gist. Please use new highWaterMark (bytes) value in sctp.connect() for larger buffers.
Also pushed version 0.19 of sctp package, addressing multiple callbacks.

Let me know your thoughts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants