sgcWebSockets P2P and WebRTC
Eight components for peer-to-peer connectivity: the NAT traversal building blocks (STUN, TURN, ICE), a WebRTC peer connection, and two signalling servers.
Two peers behind home routers cannot address each other directly. These components are the standard machinery for getting around that, and they layer: STUN discovers your public address, TURN relays when discovery is not enough, ICE tries the candidates in order, and the peer connection uses whichever won.
When to use this skill
- Find out what public address and port a NAT has assigned you
- Relay media or data through a TURN server when direct connection fails
- Gather and test ICE candidates
- Establish a WebRTC peer connection from Delphi
- Run the signalling server that lets two peers exchange offers and answers
- Run your own STUN or TURN server
Units
Six of the eight are in sgcP2P:
uses
sgcP2P; // STUN client and server, TURN client and server,
// ICE client, RTCPeerConnection
The two signalling servers, TsgcWSPServer_WebRTC and
TsgcWSPServer_RTCPeerConnection, are WebSocket protocol components and live in
sgcWebSocket_Protocols. They attach to a WebSocket server through Server,
like the other protocol components in sgcwebsockets-protocols.
Before you start, ask the developer
Use a structured question tool if your host has one, for example Claude Code's
AskUserQuestion. Otherwise ask in chat:
- Which layer do you actually need? Asking "how do I do WebRTC" usually means the peer connection plus a signalling server. Asking "what is my public IP" is just the STUN client, which is far less work.
- Are both peers Delphi, or is one a browser? A browser peer constrains the
signalling format, because the browser's
RTCPeerConnectionexpects standard SDP offers and answers. - Do you have a TURN server, or do you need to run one? Relaying costs bandwidth on whoever hosts it, so this is a deployment decision, not just a code one.
- Is a public STUN server acceptable? Many projects use a public one for discovery and only self-host TURN.
Components in this skill
| Component | Layer | Use it for |
|---|---|---|
TsgcSTUNClient |
STUN | Discover the public address a NAT gives you |
TsgcSTUNServer |
STUN | Run your own STUN service |
TsgcTURNClient |
TURN | Allocate a relay and send through it |
TsgcTURNServer |
TURN | Run your own relay |
TsgcICEClient |
ICE | Gather and prioritise connection candidates |
TsgcRTCPeerConnection |
WebRTC | The peer connection itself |
TsgcWSPServer_WebRTC |
signalling | Signalling server for WebRTC peers |
TsgcWSPServer_RTCPeerConnection |
signalling | Signalling for peer connections |
Quickstart, what is my public address
The simplest useful thing here, and often all that is wanted:
uses
sgcP2P;
FSTUN := TsgcSTUNClient.Create(Self);
if FSTUN.SendBindingRequest('stun.l.google.com', 19302) then
// the mapped address arrives through the component's events
;
GetLocalSocketBinding gives you the local side of the same socket, which is
what you compare against to work out whether you are behind a NAT at all.
Quickstart, TURN relay
TURN is allocate, then keep alive, then send. The allocation expires, so the refresh is not optional:
FTURN := TsgcTURNClient.Create(Self);
FTURN.Allocate;
FTURN.Refresh(600); // lifetime in seconds
FTURN.SendIndication('203.0.113.5', 50000, vData);
// or bind a channel first, which is cheaper per message
FTURN.ChannelBind('203.0.113.5', 50000);
FTURN.SendChannelData(vChannelId, vData);
Send indications carry the peer address on every message. A bound channel
carries a small channel id instead, so use ChannelBind and SendChannelData
when you are sending steadily to one peer.
Quickstart, WebRTC offer and answer
The peer connection follows the standard WebRTC handshake. One side offers, the other answers, and both gather candidates. Signalling, the business of getting the offer to the other peer, is what the signalling servers are for:
FPeer := TsgcRTCPeerConnection.Create(Self);
FPeer.GatherCandidates;
FPeer.CreateOffer; // the offer arrives through an event, send it on
// ... the far side calls CreateAnswer and returns its SDP ...
FPeer.WriteData('hello over the data channel');
CreateOffer and CreateAnswer do not return the SDP directly. It reaches you
through an event, because gathering is asynchronous.
Things that catch people out
- WebRTC needs signalling and the WebRTC standard does not define it. If two peers never connect, the offer or the answer is usually not reaching the other side, and the peer connection code is fine.
- A TURN allocation expires. Call
Refreshwell before the lifetime you asked for elapses, or the relay drops and the symptom looks like a network fault. - STUN alone is not enough behind symmetric NAT. That is precisely the case TURN exists for, so plan for a relay rather than assuming discovery suffices.
- The signalling servers are protocol components. They need
Serverassigned to a WebSocket server, and without it they do nothing and raise nothing. - Candidate gathering is asynchronous. Acting on the offer immediately after
calling
CreateOffergets you an incomplete candidate list.
Routing
- Find a component:
reference/components-index.mdlists every component, itsunit, and its edition, grouped by Reg module. - Uses clause: add the component's
unit:value (shown on its API page) to yourusesclause. Nothing compiles without it. - API detail:
reference/api/<Component>.mdhas the Properties, Events and Methods, each in both Delphi and C++Builder form. - Option / enum / event types: property and event types link to
reference/types/<TypeName>.md, which documents the sub-properties of option classes, the values of enums, and the parameter list of event handlers. - Examples:
examples/index.mdis the full demo catalog;examples/<Component>.mdis a focused, real usage snippet for the most-used components. - Concepts:
concepts/overview.md(getting started + uses-clause rule) andconcepts/editions-and-features.md(which components your edition includes). - Bundled resources:
concepts/resources.mdlists the browser-side assets (JavaScript, HTML, CSS) the server components serve or embed, so a browser client works without an external CDN. - Version history:
reference/history.mdlists what changed in each sgcWebSockets release.
Editions
Components are gated by edition (Professional, Enterprise, All-Access) or by a feature define. Check the edition column in the components index, or concepts/editions-and-features.md, before relying on a component.
Only public and published members are documented. Method bodies, private fields and protected members are intentionally not included.