Sanntidsstrømmesystemer (WebRTC + sanntidsdata) · leksjon

Implementere signaleringslogikk

Utvikle logikken på serversiden for å håndtere tilkoblingsforespørsler og utveksle SDP-tilbud, svar og ICE-kandidater mellom noder.

Leksjon 2 av 411 trinn

Implementere signaleringslogikk er en gratis leksjon i Sanntidsstrømmesystemer (WebRTC + sanntidsdata) på CoddyKit. Dette er leksjon 2 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Sanntidsstrømmesystemer (WebRTC + sanntidsdata), og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Sanntidsstrømmesystemer (WebRTC + sanntidsdata) inneholder totalt 4 leksjoner.

Signaleringslogikk: WebRTC-matchmakeren

Velkommen til kjernen i oppsettet av WebRTC-forbindelser! Før peers kan kommunisere direkte, trenger de en måte å utveksle viktig oppsettsinformasjon på. Det er her signaleringslogikk kommer inn.

En signaleringsserver fungerer som en midlertidig matchmaker som legger til rette for det innledende håndtrykket. Den håndterer ikke mediestrømmer direkte, men er avgjørende for å etablere forbindelsen.

  • Finne peers: Hjelper peers med å finne hverandre.
  • Utveksle metadata: Videreformidler tilbud og svar fra Session Description Protocol (SDP).
  • Dele nettverksinformasjon: Videreformidler ICE-kandidater (nettverksadresser).

Sette opp signaleringsserveren vår

Signaleringsserveren vår skal bruke WebSockets for kommunikasjon i sanntid i begge retninger. Slik setter du opp en grunnleggende server med Node.js og det populære biblioteket ws.

Denne serveren lytter etter innkommende WebSocket-forbindelser på port 8080 og danner grunnlaget for signaleringslogikken vår.

const WebSocket = require('ws');

// Create a WebSocket server instance
const wss = new WebSocket.Server({ port: 8080 });

wss.on('listening', () => {
  console.log('Signaling server listening on port 8080');
});

wss.on('connection', ws => {
  console.log('A new peer connected!');

  ws.on('message', message => {
    console.log(`Received message: ${message}`);
    // We'll add more logic here later
  });

  ws.on('close', () => {
    console.log('A peer disconnected.');
  });

  ws.on('error', error => {
    console.error('WebSocket error:', error);
  });
});

console.log('Server setup complete. Waiting for connections...');

Administrere tilkoblede peers

Når en peer kobler seg til, må serveren gi den en unik ID og holde oversikt over den. Dette gjør at vi senere kan sende meldinger til bestemte klienter.

Vi bruker en Map til å lagre aktive WebSocket-forbindelser, der hver peers ID knyttes til WebSocket-objektet dens. Serveren sender også den tildelte ID-en tilbake til klienten.

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

// Store active connections: Map<peerId, WebSocket>
const connectedPeers = new Map();

wss.on('listening', () => {
  console.log('Signaling server listening on port 8080');
});

wss.on('connection', ws => {
  // Generate a unique ID for the new peer
  const peerId = Math.random().toString(36).substring(2, 10);
  connectedPeers.set(peerId, ws);
  console.log(`Peer ${peerId} connected. Total: ${connectedPeers.size}`);

  // Send the assigned ID back to the client
  ws.send(JSON.stringify({ type: 'yourId', id: peerId }));

  ws.on('message', message => {
    console.log(`Received from ${peerId}: ${message}`);
    // Message routing logic will go here
  });

  ws.on('close', () => {
    connectedPeers.delete(peerId);
    console.log(`Peer ${peerId} disconnected. Total: ${connectedPeers.size}`);
  });

  ws.on('error', error => {
    console.error(`WebSocket error for ${peerId}:`, error);
  });
});

console.log('Server setup complete. Waiting for connections...');

Kjernen: Ruting av meldinger

Hovedoppgaven til signaleringsserveren vår er å rute meldinger mellom peers. En klient sender en melding til serveren og angir hvem som er den tiltenkte mål-peeren.

Serveren slår deretter opp WebSocket-forbindelsen til mål-peeren og videresender meldingen. Hvis målet ikke blir funnet, kan den sende en feil tilbake til avsenderen.

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });
const connectedPeers = new Map();

wss.on('listening', () => {
  console.log('Signaling server listening on port 8080');
});

wss.on('connection', ws => {
  const peerId = Math.random().toString(36).substring(2, 10);
  connectedPeers.set(peerId, ws);
  console.log(`Peer ${peerId} connected. Total: ${connectedPeers.size}`);
  ws.send(JSON.stringify({ type: 'yourId', id: peerId }));

  ws.on('message', message => {
    let parsedMessage;
    try {
      parsedMessage = JSON.parse(message);
    } catch (e) {
      console.error('Failed to parse message:', message); return;
    }

    const { targetId, type, payload } = parsedMessage;

    // If a targetId is specified, try to route the message
    if (targetId) {
      const targetPeerWs = connectedPeers.get(targetId);
      if (targetPeerWs) {
        // Forward the message to the target peer
        targetPeerWs.send(JSON.stringify({ senderId: peerId, type, payload }));
        console.log(`Routed ${type} from ${peerId} to ${targetId}`);
      } else {
        console.log(`Target peer ${targetId} not found.`);
        ws.send(JSON.stringify({ type: 'error', message: `Peer ${targetId} not found.` }));
      }
    } else {
      console.log(`Message from ${peerId} has no targetId: ${type}`);
      // Handle messages without a targetId (e.g., 'yourId' response already handled)
    }
  });

  ws.on('close', () => {
    connectedPeers.delete(peerId);
    console.log(`Peer ${peerId} disconnected. Total: ${connectedPeers.size}`);
  });

  ws.on('error', error => {
    console.error(`WebSocket error for ${peerId}:`, error);
  });
});

console.log('Server setup complete. Waiting for connections...');

Håndtere SDP-tilbud

Den første viktige meldingen i WebRTC-oppsettet er SDP-tilbudet. Peeren som ringer, genererer et tilbud som beskriver mediekapasitetene dens, og sender det til signaleringsserveren.

Serverens oppgave er å motta dette tilbudet, identifisere typen og deretter rute det til den tiltenkte peeren som svarer. Serveren endrer ikke SDP-en; den videreformidler den bare.

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });
const connectedPeers = new Map();

wss.on('listening', () => {
  console.log('Signaling server listening on port 8080');
});

wss.on('connection', ws => {
  const peerId = Math.random().toString(36).substring(2, 10);
  connectedPeers.set(peerId, ws);
  console.log(`Peer ${peerId} connected. Total: ${connectedPeers.size}`);
  ws.send(JSON.stringify({ type: 'yourId', id: peerId }));

  ws.on('message', message => {
    let parsedMessage;
    try {
      parsedMessage = JSON.parse(message);
    } catch (e) {
      console.error('Failed to parse message:', message); return;
    }

    const { targetId, type, payload } = parsedMessage;

    if (targetId) {
      const targetPeerWs = connectedPeers.get(targetId);
      if (targetPeerWs) {
        // --- NEW LOGIC: Handle SDP Offer ---
        if (type === 'offer') {
          console.log(`Received SDP Offer from ${peerId} for ${targetId}`);
          // Forward the offer to the target peer
          targetPeerWs.send(JSON.stringify({ senderId: peerId, type: 'offer', sdp: payload.sdp }));
        } else {
          // Generic forwarding for other message types (e.g., answer, ICE)
          targetPeerWs.send(JSON.stringify({ senderId: peerId, type, payload }));
          console.log(`Routed ${type} from ${peerId} to ${targetId}`);
        }
      } else {
        console.log(`Target peer ${targetId} not found.`);
        ws.send(JSON.stringify({ type: 'error', message: `Peer ${targetId} not found.` }));
      }
    } else {
      console.log(`Message from ${peerId} has no targetId: ${type}`);
    }
  });

  ws.on('close', () => {
    connectedPeers.delete(peerId);
    console.log(`Peer ${peerId} disconnected. Total: ${connectedPeers.size}`);
  });

  ws.on('error', error => {
    console.error(`WebSocket error for ${peerId}:`, error);
  });
});

console.log('Server setup complete. Waiting for connections...');

Håndtere SDP-svar

Når peeren som svarer, mottar et SDP-tilbud, genererer den et SDP-svar som beskriver dens egne mediekapasiteter, og sender det tilbake til signaleringsserveren.

Serveren mottar deretter dette svaret og ruter det tilbake til den opprinnelige peeren som ringte. Dette fullfører SDP-utvekslingen og etablerer den grunnleggende medieavtalen mellom de to peerne.

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });
const connectedPeers = new Map();

wss.on('listening', () => {
  console.log('Signaling server listening on port 8080');
});

wss.on('connection', ws => {
  const peerId = Math.random().toString(36).substring(2, 10);
  connectedPeers.set(peerId, ws);
  console.log(`Peer ${peerId} connected. Total: ${connectedPeers.size}`);
  ws.send(JSON.stringify({ type: 'yourId', id: peerId }));

  ws.on('message', message => {
    let parsedMessage;
    try {
      parsedMessage = JSON.parse(message);
    } catch (e) {
      console.error('Failed to parse message:', message); return;
    }

    const { targetId, type, payload } = parsedMessage;

    if (targetId) {
      const targetPeerWs = connectedPeers.get(targetId);
      if (targetPeerWs) {
        if (type === 'offer') {
          targetPeerWs.send(JSON.stringify({ senderId: peerId, type: 'offer', sdp: payload.sdp }));
          console.log(`Routed SDP Offer from ${peerId} to ${targetId}`);
        // --- NEW LOGIC: Handle SDP Answer ---
        } else if (type === 'answer') {
          console.log(`Received SDP Answer from ${peerId} for ${targetId}`);
          // Forward the answer to the target peer
          targetPeerWs.send(JSON.stringify({ senderId: peerId, type: 'answer', sdp: payload.sdp }));
        } else {
          // Generic forwarding for other message types (e.g., ICE)
          targetPeerWs.send(JSON.stringify({ senderId: peerId, type, payload }));
          console.log(`Routed ${type} from ${peerId} to ${targetId}`);
        }
      } else {
        console.log(`Target peer ${targetId} not found.`);
        ws.send(JSON.stringify({ type: 'error', message: `Peer ${targetId} not found.` }));
      }
    } else {
      console.log(`Message from ${peerId} has no targetId: ${type}`);
    }
  });

  ws.on('close', () => {
    connectedPeers.delete(peerId);
    console.log(`Peer ${peerId} disconnected. Total: ${connectedPeers.size}`);
  });

  ws.on('error', error => {
    console.error(`WebSocket error for ${peerId}:`, error);
  });
});

console.log('Server setup complete. Waiting for connections...');

Håndtere ICE-kandidater

Etter SDP-utvekslingen må peerne også finne de beste nettverksbanene. De genererer ICE-kandidater, som er mulige nettverksadresser.

Disse kandidatene sendes ofte og må også videreformidles av signaleringsserveren. Serveren videresender ganske enkelt hver kandidat fra avsenderen til mål-peeren når den mottas.

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });
const connectedPeers = new Map();

wss.on('listening', () => {
  console.log('Signaling server listening on port 8080');
});

wss.on('connection', ws => {
  const peerId = Math.random().toString(36).substring(2, 10);
  connectedPeers.set(peerId, ws);
  console.log(`Peer ${peerId} connected. Total: ${connectedPeers.size}`);
  ws.send(JSON.stringify({ type: 'yourId', id: peerId }));

  ws.on('message', message => {
    let parsedMessage;
    try {
      parsedMessage = JSON.parse(message);
    } catch (e) {
      console.error('Failed to parse message:', message); return;
    }

    const { targetId, type, payload } = parsedMessage;

    if (targetId) {
      const targetPeerWs = connectedPeers.get(targetId);
      if (targetPeerWs) {
        if (type === 'offer') {
          targetPeerWs.send(JSON.stringify({ senderId: peerId, type: 'offer', sdp: payload.sdp }));
          console.log(`Routed SDP Offer from ${peerId} to ${targetId}`);
        } else if (type === 'answer') {
          targetPeerWs.send(JSON.stringify({ senderId: peerId, type: 'answer', sdp: payload.sdp }));
          console.log(`Routed SDP Answer from ${peerId} to ${targetId}`);
        // --- NEW LOGIC: Handle ICE Candidate ---
        } else if (type === 'iceCandidate') {
          console.log(`Received ICE Candidate from ${peerId} for ${targetId}`);
          // Forward the ICE candidate to the target peer
          targetPeerWs.send(JSON.stringify({ senderId: peerId, type: 'iceCandidate', candidate: payload.candidate }));
        } else {
          console.log(`Unknown message type: ${type} from ${peerId}`);
        }
      } else {
        console.log(`Target peer ${targetId} not found.`);
        ws.send(JSON.stringify({ type: 'error', message: `Peer ${targetId} not found.` }));
      }
    } else {
      console.log(`Message from ${peerId} has no targetId: ${type}`);
    }
  });

  ws.on('close', () => {
    connectedPeers.delete(peerId);
    console.log(`Peer ${peerId} disconnected. Total: ${connectedPeers.size}`);
  });

  ws.on('error', error => {
    console.error(`WebSocket error for ${peerId}:`, error);
  });
});

console.log('Server setup complete. Waiting for connections...');

Sette alt sammen: En enkel signaleringsflyt

La oss følge et typisk oppsett av en samtale med serverlogikken vår:

  • Peer A (den som ringer) kobler seg til og får en ID.
  • Peer B (den som svarer) kobler seg til og får en ID.
  • Peer A oppretter et SDP-tilbud, sender det til serveren og angir Peer B som mål.
  • Serveren mottar tilbudet og ruter det til Peer B.
  • Peer B mottar tilbudet, oppretter et SDP-svar og sender det til serveren med Peer A som mål.
  • Serveren mottar svaret og ruter det til Peer A.
  • Både Peer A og Peer B genererer ICE-kandidater og sender dem til serveren med hverandre som mål.
  • Serveren mottar kandidatene og ruter dem til riktig peer.

Når all denne informasjonen er utvekslet, kan WebRTC forsøke å opprette en direkte peer-til-peer-forbindelse!

Sjekkliste for signaleringslogikk

Når du utformer eller implementerer logikken i signaleringsserveren, må du alltid sørge for at den håndterer disse viktige aspektene:

  • Unike peer-ID-er: Hver tilkoblede klient må ha en egen identifikator.
  • Tilkoblingsadministrasjon: Legg til og fjern peers når de kobler seg til eller fra.
  • Analyse av meldinger: Tolke innkommende JSON-meldinger korrekt (type, targetId, payload).
  • Meldingsruting: Videresende meldinger effektivt til den tiltenkte mottakeren.
  • Feilhåndtering: Håndtere tilfeller som ukjente mål-ID-er eller ugyldige meldinger på en kontrollert måte.

En robust signaleringsserver er avgjørende for pålitelige WebRTC-applikasjoner.

Kjapp sjekk: Flyt for signaleringsmeldinger

En klient vil opprette en WebRTC-forbindelse. Den kobler seg først til signaleringsserveren og sender deretter et SDP-tilbud for å starte samtalen. Hvilket av de følgende alternativene beskriver signaleringsserverens neste riktige handling?

Oppsummering: Bygge signaleringslogikk

I denne leksjonen utforsket vi den grunnleggende logikken på serversiden i en WebRTC-signaleringsserver. Vi lærte hvordan vi kan:

  • Sette opp en grunnleggende WebSocket-server.
  • Administrere unike ID-er for tilkoblede peers.
  • Implementere meldingsruting for å videresende SDP-tilbud, svar og ICE-kandidater.

Denne serveren fungerer som et viktig mellomledd som gjør det mulig for peers å utveksle nødvendige metadata og til slutt etablere en direkte peer-til-peer-WebRTC-forbindelse. En godt utformet signaleringsserver er ryggraden i enhver robust WebRTC-applikasjon.

Gratis å komme i gang

Lær deg Sanntidsstrømmesystemer (WebRTC + sanntidsdata) med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
12
Leksjoner
48

Ofte stilte spørsmål

Er leksjonen «Implementere signaleringslogikk» gratis?

Ja – hele teksten i «Implementere signaleringslogikk» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Sanntidsstrømmesystemer (WebRTC + sanntidsdata)-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Sanntidsstrømmesystemer (WebRTC + sanntidsdata) inneholder totalt 4 leksjoner.

Hva lærer jeg i «Implementere signaleringslogikk»?

Utvikle logikken på serversiden for å håndtere tilkoblingsforespørsler og utveksle SDP-tilbud, svar og ICE-kandidater mellom noder. Du øver på Sanntidsstrømmesystemer (WebRTC + sanntidsdata) med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Sanntidsstrømmesystemer (WebRTC + sanntidsdata)?

Ingen tidligere erfaring er nødvendig. Sanntidsstrømmesystemer (WebRTC + sanntidsdata) på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.

Hvor lang tid tar leksjonen «Implementere signaleringslogikk»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Sanntidsstrømmesystemer (WebRTC + sanntidsdata)-leksjonen?

Ja. Alle Sanntidsstrømmesystemer (WebRTC + sanntidsdata)-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Velge backend for signalering
  2. Implementere signaleringslogikk
  3. Distribuere og teste signalering
  4. Skalere signalering med rom og Redis
← Tilbake til Sanntidsstrømmesystemer (WebRTC + sanntidsdata)