Sending IceCandidates before there is a connection

3,186 views
Skip to first unread message

Sebastian Kunz

unread,
Jul 24, 2019, 4:24:38 AM7/24/19
to discuss-webrtc
Hello everyone,
I am developing a c# application that can talk to the webbrowser. The C# application creates an offer and sends it to the signaling server. The server stores that session description. Someone (in my case the browser) can now request the session description via an id. Then they exchange session descriptions and later ice candidates. This works fine for me. However there is one thing that confuses me. Right after I call createOffer() there are four icecandidates generated. What am I supposed to do with them? At this point I don't know if there is even a peer that wants to connect. Should I store the ice candidates and send them later (when there is a connection) or can I simply discard them? Can anyone explain to me why these icecandidates are triggered?

Thank you very much for your help!
Sebastian 

Neil Young

unread,
Jul 24, 2019, 4:36:36 AM7/24/19
to discuss-webrtc
Definitely not discard them :)

Well, this is how WebRTC signalling is supposed to work: You are creating an offer and forward the offer to your signalling server. Then your local stack begins to gather ICE candidates and presents that to you. You are now supposed to forward them to the other end via your signalling server too. Most implementations forward SDP and ICE "as is" in JSON objects. The separation on the signalling server is done usually by using different "rooms", but this is just another word for the ID you are using. 

My Node JS signalling server (which you could use as a reference) is using socket.io. Clients are meeting in "rooms".  The room and namespace concept of socket.io is very helpful to separate the communicating parties. Once a party is registered in a room, and there is a communication request, it sends an offer and all gathered ICE candidates straight behind the offer. The signalling server is then broadcasting these signals in the allocated room. If there is somebody, it is forwarded to him. It is also kept until the offering client is leaving, so the answerer may come later. If the offer is received at the far end, the far end generates an answer and sends it back to the signalling server in the same room. It does the same with the ICE candidates it gets from its local stack. One thing to notice probably: At the answering side you should NOT forward received ICE candidates to the local WebRTC stack, before you have created an answer. Most implementations have a little problem with this, namely all native implementations I have dealt with so far (iOS, Android). I'm usually latching incoming ICE candidates until my answer is generated and sent.

You can find my implementation here: https://dragonfly-demo.accuware.com:8443/

Join a room of your choice, e.g. from two browsers or two browser tabs and feel free to play with it. I'm only allowing two participants at a time per room.

HTH
Regards

Sebastian Kunz

unread,
Jul 24, 2019, 5:17:04 AM7/24/19
to discuss...@googlegroups.com
Oh wow. This makes a lot of sense! Thank you very much. I just saved up the ice candidates and transfered them at a later point. Works like a charm.

--

---
You received this message because you are subscribed to the Google Groups "discuss-webrtc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to discuss-webrt...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/discuss-webrtc/c3fb7999-f949-48b0-bd99-197b9bd4802d%40googlegroups.com.

Dustin

unread,
Jul 24, 2019, 6:27:18 AM7/24/19
to discuss-webrtc
Didn't know this too. So for example, at the side where I need to answer SDP, the trick is to 'save' incoming ice candidates at that side, and add them to the peerconnection when the answer is sent? 

Op woensdag 24 juli 2019 10:36:36 UTC+2 schreef Neil Young:

Philipp Hancke

unread,
Jul 24, 2019, 6:31:08 AM7/24/19
to discuss...@googlegroups.com
no, you won't get ICE candidates before you called setLocalDescription with the answer you created on that side.
https://webrtchacks.com/trickle-ice/ explains the whole process a bit more in detail.

--

---
You received this message because you are subscribed to the Google Groups "discuss-webrtc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to discuss-webrt...@googlegroups.com.

Neil Young

unread,
Jul 24, 2019, 6:44:35 AM7/24/19
to discuss-webrtc
@Philip: He means the answering side.

@Dusting: Exactly. This is what works in all cases for me. I had made the following experience with Android: In early versions I forwarded every received ICE candidate directly to the local peerConnection

await this.pc.addIceCandidate(new RTCIceCandidate(candidate))

Some connections went fine, some failed. I noticed from tons of debug logs, that in case it failed it was always the case, that ICE candidates (which worked in an OK run) have
received by me _before_ I was receiving the OFFER and was able to create my ANSWER.

From that time on I latched incoming ICE candidates until I have received the offer and processed the answer:

So I'm having a list of `remoteIceCandidates' which is filled with each received ICE candidate: (msg is the message I'm receiving from the signalling server)

if (msg.ice) {
this.remoteIcecandidates.push(msg.ice)
this.drainRemoteIceCandidates() // Check if it can be used directly
}

Then, on receiving and processing the offer I'm doing this:

await this.pc.setRemoteDescription(new RTCSessionDescription(msg.sdp))
await this.pc.setLocalDescription(await this.pc.createAnswer())
this.signalingSendMessage({ sdp: this.pc.localDescription, userId: this.state.license.userId }) // Send the answer to remote via signalling server
this.readyToForwardRemoteIceCandidates = true // This will allow the drain of gathered candidates
this.drainRemoteIceCandidates()

My drainRemoteCandidates:

// Drain all captured remote ICE candidates
drainRemoteIceCandidates = () => {
if (this.readyToForwardRemoteIceCandidates) {
let size = this.remoteIcecandidates.length
this.remoteIcecandidates.forEach(async (candidate) => {
try {
await this.pc.addIceCandidate(new RTCIceCandidate(candidate))
}
catch (err) {
console.log("Failed to add remote ICE candidate", err)
}
})
this.remoteIcecandidates = []
console.log(`${size} received remote ICE candidates added to local peer`)
}
else {
console.log("Waiting for permission to forward remote ICE candidates")
}
}

Sorry for the format mix....

HTH

Dustin

unread,
Jul 24, 2019, 7:10:08 AM7/24/19
to discuss-webrtc
Cool! Implemented this, and seems to work a lot better (Connection seems to be more clear).

I have one more question, maybe that you know that too.

I connect from WIFI to WIFI. That is going well.
I connect from WIFI to 4G that is also going well.
I connect from WIFI to WIFI (and then switch to 4G) than a bunch of ice candidates are generated and then the connection is disconnected.

What can it be?

Op woensdag 24 juli 2019 12:44:35 UTC+2 schreef Neil Young:

Neil Young

unread,
Jul 24, 2019, 7:43:33 AM7/24/19
to discuss-webrtc
The handover to 4G is for sure creating new ICE candidates, since it is an entirely new connection.  About what clients are you talking? 

I'm not exactly sure, how the WebRTC is handling this handover. I have tested here with my installation, using an iPhone and my browser in the same network via Wifi. Established the connection from the iPhone and then switched Wifi off on the iPhone. I didn't notice new ICE candidates at the browser side.

Screen got black on both sides and the browser logged:

PC ICE connection state change disconnected
PC connection state change disconnected
PC ICE connection state change failed
PC connection state change failed

I refreshed the page on the iPhone and got my "Symmetric NAT" detected message. This would explain, why - if in case handover works _generally_ - it didn't work in my case: I don't have a TURN server configured. My LTE connection did end up in ICE candidates, which would need a TURN server. Anyway, even though, this would have not worked from the media aspect, I would have expected to see new ICE candidates from remote. 

Not very helpful I know, but I would also be very surprised, if a handover would work out of the box.

EDIT: Tested right now between two browser instances (Chrome). Established the connection using Wifi, torn down Wifi, handover to ETH. No new candidates, instead disconnect.

Neil Young

unread,
Jul 24, 2019, 7:43:43 AM7/24/19
to discuss-webrtc
Reviewed my code. I'm also not processing incoming ICE before I have fully processed the ANSWER, in case I'm the OFFERER. Just for completeness:

if (msg.sdp.type == 'answer') {
await this.pc.setRemoteDescription(new RTCSessionDescription(msg.sdp))
this.readyToForwardRemoteIceCandidates = true
this.drainRemoteIceCandidates()
}

Am Mittwoch, 24. Juli 2019 13:10:08 UTC+2 schrieb Dustin:

Dustin

unread,
Jul 24, 2019, 7:59:22 AM7/24/19
to discuss-webrtc
I'm talking about iPhone's / iPads. So its not an issue at my side (I thought that WebRTC is handling it for me).
So I can create a new offer when internet is back and disconnect is called I think?

Op woensdag 24 juli 2019 13:43:33 UTC+2 schreef Neil Young:

Philipp Hancke

unread,
Jul 24, 2019, 8:00:19 AM7/24/19
to discuss...@googlegroups.com
If you are signaling ice candidates before offer/answer you are doing *something* wrong.
There was a recent spec change which clarified the situation here (https://github.com/w3c/webrtc-pc/issues/2199) but onicecandidate never fired before setLocalDescription was complete.

--

---
You received this message because you are subscribed to the Google Groups "discuss-webrtc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to discuss-webrt...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages