Strömningssystem i realtid (WebRTC + live-data) · Lektion

Implementera signaleringslogik

Utveckla logiken på serversidan för att hantera anslutningsförfrågningar och utbyta SDP-erbjudanden, SDP-svar och ICE-kandidater mellan peers.

Lektion 2 av 411 steg

Implementera signaleringslogik är en gratis lektion i Strömningssystem i realtid (WebRTC + live-data) på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Strömningssystem i realtid (WebRTC + live-data), och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Strömningssystem i realtid (WebRTC + live-data) innehåller totalt 4 lektioner.

Signaleringslogik: WebRTC:s matchmaker

Välkomna till kärnan i WebRTC-anslutningens uppsättning! Innan peers kan kommunicera direkt behöver de ett sätt att utbyta viktig information för uppkopplingen. Det är här signaleringslogik kommer in.

En signaleringsserver fungerar som en tillfällig matchmaker och underlättar den inledande handskakningen. Den hanterar inte medieströmmar direkt, men är avgörande för att upprätta anslutningen.

  • Hitta peers: Hjälper peers att hitta varandra.
  • Utbyta metadata: Vidarebefordrar erbjudanden och svar enligt Session Description Protocol (SDP).
  • Dela nätverksinformation: Vidarebefordrar ICE-kandidater (nätverksadresser).

Konfigurera vår signaleringsserver

Vår signaleringsserver använder WebSockets för dubbelriktad kommunikation i realtid. Så här konfigurerar ni en grundläggande server med Node.js och det populära biblioteket ws.

Servern lyssnar efter inkommande WebSocket-anslutningar på port 8080, vilket utgör grunden för vår signaleringslogik.

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...');

Hantera anslutna peers

När en peer ansluter måste vår server ge den ett unikt ID och hålla reda på den. Det gör att vi senare kan skicka meddelanden till specifika klienter.

Vi använder en Map för att lagra aktiva WebSocket-anslutningar och koppla varje peers ID till dess WebSocket-objekt. Servern skickar också tillbaka det tilldelade ID:t till 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...');

Kärnan: dirigera meddelanden

Vår signaleringsservers huvudsakliga uppgift är att dirigera meddelanden mellan peers. En klient skickar ett meddelande till servern och anger vilken mål-peer som ska ta emot det.

Servern slår sedan upp mål-peerens WebSocket-anslutning och vidarebefordrar meddelandet. Om mål-peeren inte hittas kan servern skicka tillbaka ett fel till avsändaren.

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...');

Hantera SDP-erbjudanden

Det första viktiga meddelandet när WebRTC konfigureras är SDP Offer. Den peer som ringer skapar ett erbjudande som beskriver dess mediekapacitet och skickar det till signaleringsservern.

Vår servers uppgift är att ta emot erbjudandet, identifiera dess typ och sedan dirigera det till den avsedda peer som svarar. Servern ändrar inte SDP:t, utan vidarebefordrar det bara.

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...');

Hantera SDP-svar

När den peer som svarar tar emot ett SDP-erbjudande skapar den ett SDP Answer som beskriver dess egen mediekapacitet och skickar det tillbaka till signaleringsservern.

Servern tar sedan emot svaret och dirigerar det tillbaka till den ursprungliga peer som ringde. Detta slutför SDP-utbytet och upprättar det grundläggande medieavtalet mellan de två peers.

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...');

Hantera ICE-kandidater

Efter SDP-utbytet behöver peers också hitta de bästa nätverksvägarna. De genererar ICE Candidates, som är möjliga nätverksadresser.

Dessa kandidater skickas ofta och måste också vidarebefordras av signaleringsservern. Servern skickar helt enkelt varje kandidat från avsändaren till mål-peeren så snart den anländer.

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...');

Sätt ihop allt: ett enkelt signaleringsflöde

Låt oss följa en typisk uppsättning av ett samtal med vår serverlogik:

  • Peer A (den som ringer) ansluter och får ett ID.
  • Peer B (den som svarar) ansluter och får ett ID.
  • Peer A skapar ett SDP-erbjudande och skickar det till servern, adresserat till Peer B.
  • Servern tar emot erbjudandet och dirigerar det till Peer B.
  • Peer B tar emot erbjudandet, skapar ett SDP-svar och skickar det till servern, adresserat till Peer A.
  • Servern tar emot svaret och dirigerar det till Peer A.
  • Både Peer A och Peer B genererar ICE-kandidater och skickar dem till servern, adresserade till varandra.
  • Servern tar emot kandidaterna och dirigerar dem till rätt peer.

När all denna information har utbytts kan WebRTC försöka upprätta en direkt peer-to-peer-anslutning!

Checklista för signaleringslogik

När ni utformar eller implementerar logiken för signaleringsservern ska ni alltid se till att den hanterar följande viktiga delar:

  • Unika peer-ID:n: Varje ansluten klient måste ha en unik identifierare.
  • Anslutningshantering: Lägg till och ta bort peers när de ansluter och kopplar från.
  • Meddelandeparsning: Tolka inkommande JSON-meddelanden korrekt (type, targetId, payload).
  • Meddelandedirigering: Vidarebefordra meddelanden effektivt till den avsedda mottagaren.
  • Felhantering: Hantera på ett bra sätt situationer som okända mål-ID:n eller felaktigt formaterade meddelanden.

En robust signaleringsserver är avgörande för tillförlitliga WebRTC-applikationer.

Snabbtest: Signaleringsmeddelandets flöde

En klient vill upprätta en WebRTC-anslutning. Först ansluter den till signaleringsservern och skickar sedan ett SDP-erbjudande för att initiera samtalet. Vilket av följande beskriver signaleringsserverns nästa korrekta åtgärd?

Sammanfattning: Bygga signaleringslogik

I den här lektionen undersökte vi den viktiga logiken på serversidan för en WebRTC-signaleringsserver. Vi lärde oss att:

  • Konfigurera en grundläggande WebSocket-server.
  • Hantera unika ID:n för anslutna peers.
  • Implementera meddelandedirigering för att vidarebefordra SDP-erbjudanden, SDP-svar och ICE-kandidater.

Den här servern fungerar som en viktig mellanhand och gör det möjligt för peers att utbyta de metadata som krävs för att till slut upprätta en direkt peer-to-peer-WebRTC-anslutning. En väl utformad signaleringsserver är ryggraden i alla robusta WebRTC-applikationer.

Gratis att börja

Lär dig Strömningssystem i realtid (WebRTC + live-data) med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
12
Lektioner
48

Vanliga frågor

Är lektionen ”Implementera signaleringslogik” gratis?

Ja – hela texten till ”Implementera signaleringslogik” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Strömningssystem i realtid (WebRTC + live-data), kan Ni uppgradera till CoddyKit PRO. Kursen i Strömningssystem i realtid (WebRTC + live-data) innehåller totalt 4 lektioner.

Vad lär jag mig i ”Implementera signaleringslogik”?

Utveckla logiken på serversidan för att hantera anslutningsförfrågningar och utbyta SDP-erbjudanden, SDP-svar och ICE-kandidater mellan peers. Ni övar på Strömningssystem i realtid (WebRTC + live-data) med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Strömningssystem i realtid (WebRTC + live-data)?

Du behöver inga förkunskaper. Utbildningen i Strömningssystem i realtid (WebRTC + live-data) på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Implementera signaleringslogik”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Strömningssystem i realtid (WebRTC + live-data)-lektionen?

Ja. Varje Strömningssystem i realtid (WebRTC + live-data)-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Välja backend för signalering
  2. Implementera signaleringslogik
  3. Distribuera och testa signalering
  4. Skala signalering med rum och Redis
← Tillbaka till Strömningssystem i realtid (WebRTC + live-data)