Use this page when you need a real relay, not a STUN-only best-case path.
TURN is what gets you through the ugly NAT cases that direct peer-to-peer never will. icey's TURN server is built on the same runtime and socket layer as the rest of the library, so you can run relay, signalling, and application code in one process if you want to.
If TURN is new territory, read STUN first. TURN messages are STUN messages with allocation and relay semantics layered on top.
A TURN server that works in practice needs four things right:
realmThat last one is where people usually waste time.
If your process binds to a private address but clients are out on the public internet, set externalIP correctly or the server will advertise the wrong relay address.
#include "icy/turn/server/server.h"
using namespace icy;
class MyServer : public turn::ServerObserver {
public:
turn::Server server;
MyServer(const turn::ServerOptions& opts)
: server(*this, opts) {}
turn::AuthenticationState authenticateRequest(turn::Server*, turn::Request& req) override;
void onServerAllocationCreated(turn::Server*, turn::IAllocation*) override {}
void onServerAllocationRemoved(turn::Server*, turn::IAllocation*) override {}
};
turn::ServerOptions opts;
opts.realm = "example.com";
opts.listenAddr = net::Address("0.0.0.0", 3478);
opts.externalIP = "203.0.113.1";
opts.enableUDP = true;
opts.enableTCP = true;
MyServer srv(opts);
srv.server.start();That gives you the actual TURN server. The rest of the work is auth policy and deployment detail.
icey's TURN server uses the standard long-term credential path from RFC 5389.
The usual flow is:
Allocate401USERNAME, REALM, NONCE, and MESSAGE-INTEGRITYYour observer decides what to do:
AuthorizedNotAuthorizedQuotaReachedAuthenticatingThat last state matters if you want to do real async auth without blocking the loop.
externalIP RuleThis is the thing to get right before anything else.
Use externalIP when:
10.x, 172.16-31.x, or 192.168.xDo not leave it empty and hope ICE will work it out later. TURN needs to tell the client the truth up front.
icey can auto-grant permissions for local and RFC 1918 peers:
opts.enableLocalIPPermissions = true;That is useful for development and mixed LAN/public deployments.
It is not a substitute for understanding TURN permissions. It is a convenience for the cases where local probes and local peers are normal.
The included sample is still the fastest way to prove the server path works:
You can hit it with coturn's turnutils_uclient:
turnutils_uclient -u username -w password 127.0.0.1That gives you a much faster signal than trying to debug a full browser stack before the relay basics are sound.
externalIP to the public relay IP the client should use.CreatePermission and relay traffic as part of the real data plane, not as optional control-plane noise.turnserver for the sample binary