@thzero/library_server_transport_datachannel
v0.18.6
Published
  [;
channel.onConnect((error => {
if (error) {
console.error(error.message)
return;
}
channel.onMessage((data => {
console.log(`You got the message ${data}`);
});
channel.emit('chat message', 'a short message sent to the server');
});server.js
import Server from '@thzero/library_server_transport_datachannel/server.js'
const io = Server.initialize({ /* options */});
io.listen(port); // default port is 9208
io.onConnection(channel => {
channel.onDisconnect(() => {
console.log(`${channel.id} got disconnected`)
});
channel.on('chat message', data => {
console.log(`got ${data} from "chat message"`);
// replay back to channel.
channel.emit('chat message', data);
});
});AutoManageBuffering option
By default the RTCDataChannel queues data if it can't be send directly. This is very bad for multiplayer games, since we do not want to render old state.
The option autoManageBuffering is available and set to true by default. If autoManageBuffering is on, @thzero/library_server_transfer_datachannel will prefer to drop messages instead of adding them to the send queue.
The problem with the queue while gaming?
From Geckos.io...
If you send 30Kbytes @60fps and the client only has a 10Mbit connection, he can never receive all messages. So it is necessary to drop some of them, which will be done automatically with autoManageBuffering.
Another good solution to this problem would be to decrease the send rate for that specific client. Use the new channel.onDrop(drop => {}) method to track dropped messages. If, for example, you notice that 20% of the messages for a specific client are dropped, decrease the send rate.
Authorization and Authentication
Like Geckos.io, the client is able to send a authorization header with the connection request. If the authorization fails, the server will respond with 401 (unauthorized).
Whatever you add to the option authorization (must be a string) will be sent as a Authorization request header. You could, for example, send Basic base64-encoded credentials, Bearer tokens or a simple string, as in the example below.
Read more about HTTP authentication here: .
server.js
Example from Geckos.io convert to @thzero/library_server_transport_datachannel.
import Server from '@thzero/library_server_transport_datachannel/server.js'
const io = Server.initialize({
/**
* A async function to authenticate and authorize a user.
* @param auth The authentication token
* @param request The incoming http request
* @param response The outgoing http response
*/
authorization: async (auth, request, response) => {
const token = auth.split(' ') // ['Yannick', '12E45'];
const username = token[0] // 'Yannick';
const password = token[1] // '12E45';
// Use "request.connection.remoteAddress" to get the users ip.
// ("request.headers['x-forwarded-for']" if your server is behind a proxy)
// add a custom response header if you want
response.setHeader(
'www-authenticate',
'Bearer realm="example", error="invalid_token", error_description="The access token expired"'
);
// reach out to a database if needed (this code is completely fictitious)
const user = await database.getByName(username);
// whatever you return here, will later be accessible via channel.userData to authenticate the user
if (user.username === username && user.password === password)
return { username: user.username, level: user.level, points: user.points };
// if you return true, you will authorize the connection, without adding any data to channel.userData
return true;
// if you return false, the server will respond with 401 (unauthorized)
return false;
// if you return a number between 100 and 599, the server will respond with the respective HTTP status code
return 400; // will return 400 (Bad Request)
return 404; // will return 404 (Not Found)
return 500; // will return 500 (Internal Server Error)
// and so on ...
},
cors: { allowAuthorization: true } // required if the client and server are on separate domains
});
io.onConnection((channel) => {
console.log(channel.userData); // { username: 'Yannick', level: 13, points: 8987 }
});Troubleshooting
Some general troubleshooting from Geckos.io that apply with @thzero/library_server_transport_datachannel.
- If the llibrary does not run on
http://localhost:PORT/? Tryhttp://127.0.0.1:PORT/instead. - If server listener is listening but never establishes a connection, that might be due to your machine not exposing OPENSSL environment variables (see https://github.com/geckosio/geckos.io/pull/260). To add them try the following:
- Find the path to OpenSSL:
brew --prefix openssl- Set the environment variables. You can set the environment variables in your shell by adding the following lines to your shell profile file (usually ~/.bash_profile, ~/.bashrc, or ~/.zshrc for Zsh):
export OPENSSL_ROOT_DIR=/usr/local/opt/[email protected]
export OPENSSL_CRYPTO_LIBRARY=/usr/local/opt/[email protected]/lib
export OPENSSL_INCLUDE_DIR=/usr/local/opt/[email protected]/includeReplace /usr/local/opt/[email protected] with the path you got from the second step. Then, save the file and restart your terminal for the changes to take effect.
- Check that the environment variables are set (maybe you'll need to restart your terminal):
echo $OPENSSL_ROOT_DIR
echo $OPENSSL_CRYPTO_LIBRARY
echo $OPENSSL_INCLUDE_DIR- Done ✅
Deployment
Your application has to be deployed to a server that forwards the following to your application:
- All traffic on ports 9208/tcp (or another port you define).
- Used for peer signaling. The peer connection will be on a random port between 1025-65535/udp.
- All traffic on ports 1025-65535/udp.
ICE Servers
There is a default list of ICE servers for development and testing. For production, it is suggested for the application to have their own STUN and TURN servers.
import Server from '@thzero/library_server_transport_datachannel/server.js';
import { defaultIceServers } from '@thzero/library_transport_datachannel/iceServers.js';
// Use an empty array if you are developing locally
// Use the default iceServers if you are testing it on your server
const io = Server.initialize({ iceServers: null, TESTING_LOCALLY ? [] : defaultIceServers })
