Table of contents
- When and why you need a TURN server
- Issues with Different Internet Connections
- NAT traversal problems
- Role of TURN servers
- Configuring the Simple Peer with TURN server
- Setting Up TURN server credentials
- Modifying the config Option in Simple Peer to add a TURN server
- Verifying connections via TURN server
- Verifying TURN server connection in your Application
- Common Issues and How to Resolve Them
- 1. Connection is established when on the same network but fails when switched to different internet connections
This article was originally written on the Metered.ca Blog: Simple Peer Tutorial: Add TURN Server for Video, DataChannel
Simple Peer is a JavaScript Library for implementing peer-to-peer communication
This library is built on top of webrtc, at its basic implementation the Simple Peer provides a straightforward API for setting up P2P communication. It has the following features
Signalling
Connection management
Media Streams
Data Channels
When and why you need a TURN server
In Peer-to-Peer communications involving webrtc libraries such as Simple peer, it is important to establish a direct communication channel.
Due to various network configurations and restrictions such as NAT routers and firewalls, the direct connections is not possible
This where TURN servers come into play, The simple peer does not come with a TURN server, and you need to add it from a turn server provider
Issues with Different Internet Connections
Symmetric NAT: Some networks use symmetric NATs which makes it impossible to have peer to peer direct connectivity without a TURN server
Firewalls: Many firewalls block incoming connections from external sources
Carrier Grade NAT: Mobile networks and Internet service providers use Carrier Grade NAT which adds another layer of complexity and thus makes it difficult to establish connection without a TURN server
NAT traversal problems
IP address Obfuscation
Port mapping complications:
Blocked connections
Role of TURN servers
Data Relay
Bypassing restrictions
Enhanced Reliability
API: TURN server management with powerful API. You can do things like Add/ Remove credentials via the API, Retrieve Per User / Credentials and User metrics via the API, Enable/ Disable credentials via the API, Retrive Usage data by date via the API.
Global Geo-Location targeting: Automatically directs traffic to the nearest servers, for lowest possible latency and highest quality performance. less than 50 ms latency anywhere around the world
Servers in all the Regions of the world: Toronto, Miami, San Francisco, Amsterdam, London, Frankfurt, Bangalore, Singapore,Sydney, Seoul, Dallas, New York
Low Latency: less than 50 ms latency, anywhere across the world.
Cost-Effective: pay-as-you-go pricing with bandwidth and volume discounts available.
Easy Administration: Get usage logs, emails when accounts reach threshold limits, billing records and email and phone support.
Standards Compliant: Conforms to RFCs 5389, 5769, 5780, 5766, 6062, 6156, 5245, 5768, 6336, 6544, 5928 over UDP, TCP, TLS, and DTLS.
Multi‑Tenancy: Create multiple credentials and separate the usage by customer, or different apps. Get Usage logs, billing records and threshold alerts.
Enterprise Reliability: 99.999% Uptime with SLA.
Enterprise Scale: With no limit on concurrent traffic or total traffic. Metered TURN Servers provide Enterprise Scalability
5 GB/mo Free: Get 5 GB every month free TURN server usage with the Free Plan
Runs on port 80 and 443
Support TURNS + SSL to allow connections through deep packet inspection firewalls.
Supports both TCP and UDP
Free Unlimited STUN
Configuring the Simple Peer with TURN server
When you are creating peer to peer connections across different internet connections, that are behind strict NAT and firewall rules. Incorporating a TURN server is necessary to smooth connectivity
Setting Up TURN server credentials
Step 1: go to Metered.ca/stun-turn
Go to Metered.ca/stun-turn and create a free account by clicking on the Get Started
button to create a free account
Step 2: Create a TURN server credential
After you sign up for a free account you will land up in the dashboard
here click on the Click Here to Generate your First Credential
to create your turn server credential
Step 3 Copy the credentials
After you create your first credential you will get your username and password.
Click on the instructions
button to get your ice server array
Copy this ICE server array to paste in the Simple Peer in order to add turn server to your Simple Peer
Modifying the config
Option in Simple Peer to add a TURN server
Simple peer lets you customize the ICE array that is the Interactive Connectivity Establishment array during the connection process
Here I have converted the default config
option to add the Metered.ca TURN server
{
initiator: false,
channelConfig: {},
channelName: '<random string>',
config: { iceServers: [
{
urls: "stun:stun.relay.metered.ca:80",
},
{
urls: "turn:global.relay.metered.ca:80",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
{
urls: "turn:global.relay.metered.ca:80?transport=tcp",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
{
urls: "turn:global.relay.metered.ca:443",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
{
urls: "turns:global.relay.metered.ca:443?transport=tcp",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
]},
offerOptions: {},
answerOptions: {},
sdpTransform: function (sdp) { return sdp },
stream: false,
streams: [],
trickle: true,
allowHalfTrickle: false,
wrtc: {}, // RTCPeerConnection/RTCSessionDescription/RTCIceCandidate
objectMode: false
}
Code Example: Adding TURN server to Simple Peer
Below is a comprehensive example on how to integrate the TURN server with the Simple Peer
HTML (index.html
)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Simple Peer with TURN Server</title>
<style>
#outgoing {
width: 600px;
word-wrap: break-word;
white-space: normal;
}
</style>
</head>
<body>
<form>
<textarea id="incoming" placeholder="Paste remote signal data here"></textarea>
<button type="submit">Submit</button>
</form>
<pre id="outgoing"></pre>
<video id="remoteVideo" autoplay playsinline></video>
<script src="https://unpkg.com/simple-peer/simplepeer.min.js"></script>
<script>
// TURN server configuration
const peerConfig = {
iceServers: [
{
urls: "stun:stun.relay.metered.ca:80",
},
{
urls: "turn:global.relay.metered.ca:80",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
{
urls: "turn:global.relay.metered.ca:80?transport=tcp",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
{
urls: "turn:global.relay.metered.ca:443",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
{
urls: "turns:global.relay.metered.ca:443?transport=tcp",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
]
};
// Create a new peer
const peer = new SimplePeer({
initiator: location.hash === '#1', // Use #1 in URL to set as initiator
trickle: false, // Disable trickle ICE
config: peerConfig // Include the TURN server configuration
});
// Handle errors
peer.on('error', err => console.error('Error:', err));
// Generate signaling data
peer.on('signal', data => {
document.querySelector('#outgoing').textContent = JSON.stringify(data);
});
// Handle form submission to input remote signaling data
document.querySelector('form').addEventListener('submit', ev => {
ev.preventDefault();
const incomingData = document.querySelector('#incoming').value;
try {
peer.signal(JSON.parse(incomingData));
} catch (e) {
console.error('Invalid signaling data:', e);
}
});
// Once connected, send a message
peer.on('connect', () => {
console.log('Peer connected!');
peer.send('Hello from peer!');
});
// Receive messages from remote peer
peer.on('data', data => {
console.log('Received message:', data.toString());
});
// Handle receiving a remote stream (e.g., video)
peer.on('stream', stream => {
const videoElement = document.getElementById('remoteVideo');
if ('srcObject' in videoElement) {
videoElement.srcObject = stream;
} else {
videoElement.src = window.URL.createObjectURL(stream); // Fallback for older browsers
}
});
// Optional: Get local media stream (audio/video) and add to peer
navigator.mediaDevices
.getUserMedia({ video: true, audio: true })
.then(stream => {
peer.addStream(stream);
})
.catch(err => {
console.error('Failed to get local stream:', err);
});
</script>
</body>
</html>
Explanation of the code:
Adding Simple Peer via CDN
TURN server Configuration
- The
PeerConfig
Object includes the TURN server configuration that we created above on Metered.ca TURn server
- The
Creating a Peer Instance:
The
initiator
flag is set based URL hashtrickle is set to false in order to gather all ICE candidates before signalling, this might increase connection time but simplifies the signalling process
Handling Signalling data
The signal event outputs signlling data that needs to be sent to the remote peer
users are manually copying and pasting the signalling data in the above example for simplicity
Establishing connections
the connect event indicates that the peer to peer connection is established.
You can start sending data by using
peer.send()
Handling streams
The code requests the user media devices that is camera and microphones
the local steams is added to peer.addStream(stream)
the stream event is then used to display the remote video stream
Verifying connections via TURN server
To make sure that the integrgration of TURN server is successful and working with Simple Peer correctly
It is essential to test the setup and confirm that the connections are established using the turn server
Using the Metered TURN server Testing Tool
The Metered.ca provides a handy online tool to test your TURN server configuration
you can access the link here: TURN Server Testing tool
Verifying TURN server connection in your Application
Even if you have a successful TURN server running, it is important that your application uses the TURN server to establish the connection properly
How to verify TURN server utilization
Inspect ICE candidates
- You can remove the
stun
candidates from the ICE array in order to send all the data through the TURN server
- You can remove the
Edit the ICE array
- Edit the ICE array to delete the STUN server urls
[
//Here we have commented the STUN server urls so that the STUN //servers will not be used and all the data will be transmitted //through turn servers only
// {
// urls: "stun:stun.relay.metered.ca:80",
// },
{
urls: "turn:global.relay.metered.ca:80",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
{
urls: "turn:global.relay.metered.ca:80?transport=tcp",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
{
urls: "turn:global.relay.metered.ca:443",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
{
urls: "turns:global.relay.metered.ca:443?transport=tcp",
username: "87a3a559dd4cff69c4e5eae5",
credential: "O2WMwfz+PFmqp4kV",
},
]
Now, if still your video / audio calling is working then you have successfully implemented TURN servers in your application
Common Issues and How to Resolve Them
1. Connection is established when on the same network but fails when switched to different internet connections
When two peers are on the same network establishing a peer to peer connection is easy because there is no Network Addressee Translation (NAT) traversal and firewall
When these peers are across the internet then there are NAT and firewall issues that block peer to peer direct connectivity
to solve this problem simply add a turn server to your ICE array in the simple peer configuration
we have already shown you how to do this above: Modifying the config
Option in Simple Peer to add a TURN server