In your case, you need STUN. Most clients will be behind NAT, so you need STUN to get the clients public IP. But if both your clients were not behind NAT, then you wouldn't need STUN. More generally, no, a STUN server is not strictly required. I know this because I successfully connected 2 WebRTC peers without a stun server. I used the example code from aiortc, a python WebRTC/ ORTC library where both clients were running locally on my laptop. The signalling channel used my manual copy-pasting. I literally copied the SD (session description) from the one peer to the other. Then, copied the SD from the 2nd peer to the 1st peer once again.
From the ICE RFC (RFC8445), which WebRTC uses
An ICE agent SHOULD gather server-reflexive and relayed candidates.
However, use of STUN and TURN servers may be unnecessary in certain
networks and use of TURN servers may be expensive, so some
deployments may elect not to use them.
It's not clear that STUN is a requirement for ICE, but the above says it may be unnecessary.
However, signalling has nothing to do with it. This question actually stems from not understanding what STUN does, and how STUN interplays with signalling. I would argue the other 3 answers here do not actually answer these 2 concerns.
Pre-requisite: Understand the basic concepts of NAT. STUN is a tool to go around NAT, so you have to understand it.
Signalling: Briefly, in WebRTC you need to implement your own signalling strategy. You can manually type the local session description created by one peer in the other peer, use WebSockets, socket.io, or any other methods (I saw a joke that smoke signals can be used, but how are you going to pass the following session description (aka. SDP message) through a smoke signal...). Again, I copy pasted something very similar to below:
v=0
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com
s=
c=IN IP4 host.anywhere.com
t=0 0
m=audio 49170 RTP/AVP 0
a=rtpmap:0 PCMU/8000
m=video 51372 RTP/AVP 31
a=rtpmap:31 H261/90000
m=video 53000 RTP/AVP 32
a=rtpmap:32 MPV/90000
When both peers are not behind NAT, you don't need a STUN server, as the IP addresses located in the session description (the c=
field above, known as connection data) generated by each peer would be enough for each peer to send datagrams or packets to each other. In the example above, they've provided the domain name instead of IP address, host.anywhere.com
, but this can be resolved to an A record. (Study DNS for more information).
Why don't you need a STUN server in this case? From RFC8445:
There are different types of candidates; some are derived from physical or logical network interfaces, and others are discoverable via STUN and TURN.
If you're not using NAT, the client already knows the IP address which peers can directly address, so the additional ICE candidates that STUN would generate would not be helpful (it would just give you the same IP address you already know about).
But when a client is behind a NAT, the IP they think they won't help a peer contact them. Its like telling you my ip address is 192.168.1.235
, it really is, but its my private IP. The NAT might be on the router, and your client may have no way of asking for the public IP. So STUN is a tool for dealing with this. Specifically,
It provides a means for an endpoint to determine the IP address and port allocated by a NAT that corresponds to its private IP address and port.
STUN basically lets the client find out what the IP address. If you were hosting a Call of Duty server from your laptop, and port forwarded a port to your machine in the router settings, you still had to look up your public IP address from a website like https://whatismyipaddress.com/. STUN lets a client do this for itself, without you accessing a browser.
Finally, how does STUN interplay with signalling?
The ICE candidates are generated locally and with the help of STUN (to get client public IP addresses when they're behind NAT) and even TURN. Session descriptions are sent to the peer using the signalling channel. If you don't use STUN, you might find that the ICE candidates generated that is tried by ICE all fail, and a connection (other than the signalling channel) does not successfully get created.