Start · Sprachen · JavaScript · Referenz · RTCPeerConnection.signalingState

RTCPeerConnection.signalingState

Eigenschaft

Schreibgeschützte Eigenschaft, die den aktuellen Zustand des Offer/Answer-Signalisierungsprozesses der lokalen <code>RTCPeerConnection</code> als Zeichenkette liefert.

seit JavaScript Baseline: alle modernen Browser (Chrome 26+, Firef Kategorie: webapi

Signatur

RTCPeerConnection.signalingState

Beschreibung

Die Eigenschaft signalingState zeigt, an welcher Stelle des SDP-Austauschs (Offer/Answer) die lokale Seite einer RTCPeerConnection gerade steht. Der Wert ändert sich durch Aufrufe von setLocalDescription() und setRemoteDescription(). Bei jeder Änderung wird das Ereignis signalingstatechange ausgelöst.

Mögliche Werte:

  • "stable": Es läuft kein Austausch. Entweder wurde noch nichts ausgehandelt, oder Offer und Answer sind abgeschlossen.
  • "have-local-offer": Ein lokales Offer wurde mit setLocalDescription() gesetzt.
  • "have-remote-offer": Ein entferntes Offer wurde mit setRemoteDescription() gesetzt.
  • "have-local-pranswer": Auf ein entferntes Offer wurde lokal eine vorläufige Antwort (pranswer) gesetzt.
  • "have-remote-pranswer": Auf ein lokales Offer ist eine vorläufige Antwort der Gegenseite eingetroffen.
  • "closed": Die Verbindung wurde mit close() geschlossen.

Praktisch ist die Eigenschaft vor allem, um Race Conditions bei der Neuverhandlung zu verhindern, etwa bei „Glare“, wenn beide Seiten gleichzeitig ein Offer senden. Sie ist außerdem die Grundlage des „Perfect Negotiation“-Musters. Mit signalingState prüfen Sie, ob ein neues Offer gerade zulässig ist oder ob ein eingehendes Offer mit einem eigenen kollidiert.

Verwechseln Sie signalingState nicht mit connectionState oder iceConnectionState. Diese beiden beschreiben den Transport- bzw. ICE-Zustand. signalingState betrifft nur die Aushandlung der Sitzungsbeschreibung.

Rückgabewert

Typ
"stable" | "have-local-offer" | "have-remote-offer" | "have-local-pranswer" | "have-remote-pranswer" | "closed"
Beschreibung
Eine Zeichenkette aus der Aufzählung RTCSignalingState, die den aktuellen Signalisierungszustand beschreibt.

Beispiele

Zustandswechsel beim Offer/Answer-Ablauf beobachten

const pc = new RTCPeerConnection();

pc.addEventListener('signalingstatechange', () => {
  console.log('Neuer Zustand:', pc.signalingState);
});

console.log('Start:', pc.signalingState);

const demo = async () => {
  pc.createDataChannel('chat'); // damit das Offer Inhalt hat
  const offer = await pc.createOffer();
  await pc.setLocalDescription(offer);
};

demo();
Start: stable Neuer Zustand: have-local-offer

Kompletter lokaler Austausch zwischen zwei Peers

const alice = new RTCPeerConnection();
const bob = new RTCPeerConnection();
alice.createDataChannel('daten');

const log = (name, pc) => console.log(`${name}: ${pc.signalingState}`);

const negotiate = async () => {
  const offer = await alice.createOffer();
  await alice.setLocalDescription(offer);
  log('Alice', alice);

  await bob.setRemoteDescription(offer);
  log('Bob', bob);

  const answer = await bob.createAnswer();
  await bob.setLocalDescription(answer);
  log('Bob', bob);

  await alice.setRemoteDescription(answer);
  log('Alice', alice);

  alice.close();
  log('Alice', alice);
};

negotiate();
Alice: have-local-offer Bob: have-remote-offer Bob: stable Alice: stable Alice: closed

Perfect Negotiation: Offer-Kollision erkennen

// signaling ist ein beliebiger Kanal, z. B. ein WebSocket-Wrapper
const setupNegotiation = (pc, signaling, polite) => {
  let makingOffer = false;

  pc.onnegotiationneeded = async () => {
    try {
      makingOffer = true;
      await pc.setLocalDescription();
      signaling.send({ description: pc.localDescription });
    } finally {
      makingOffer = false;
    }
  };

  signaling.onmessage = async ({ description }) => {
    if (!description) return;
    const collision = description.type === 'offer' &&
      (makingOffer || pc.signalingState !== 'stable');

    // Die unhöfliche Seite ignoriert kollidierende Offers
    if (!polite && collision) return;

    await pc.setRemoteDescription(description); // löst ggf. implizites Rollback aus
    if (description.type === 'offer') {
      await pc.setLocalDescription();
      signaling.send({ description: pc.localDescription });
    }
  };
};

// Wichtig · Fallstricke

  • Schreibgeschützt: Eine Zuweisung an signalingState wird ignoriert bzw. wirft im Strict Mode einen TypeError. Den Zustand ändern Sie nur über setLocalDescription(), setRemoteDescription() oder close().
  • Ungültige Übergänge: Setzen Sie z. B. im Zustand "have-local-offer" ein weiteres entferntes Offer, schlägt setRemoteDescription() mit einem InvalidStateError fehl. Die Ausnahme sind Browser, die ein implizites Rollback unterstützen. Prüfen Sie den Zustand deshalb vor Neuverhandlungen.
  • Rollback: Mit setLocalDescription({ type: 'rollback' }) gelangt die Verbindung aus einem Offer-Zustand zurück nach "stable". Ältere Safari-Versionen unterstützten das nur eingeschränkt.
  • Asynchronität: Der Zustand ändert sich erst, wenn das Promise von setLocalDescription() bzw. setRemoteDescription() erfüllt ist. Ein direktes Auslesen im selben Tick liefert noch den alten Wert.
  • pranswer: Die Zustände mit vorläufiger Antwort kommen in der Praxis selten vor und werden nicht von jeder Signalisierungslösung genutzt.
  • Nach close() bleibt der Wert dauerhaft "closed". Die Instanz lässt sich nicht wiederverwenden.