Dela via


Skapa en kodströmningsapp i realtid med hjälp av Socket.IO och vara värd för den i Azure

Det kan vara svårt att skapa en realtidsupplevelse som funktionen för samskapande i Microsoft Word .

Genom sina lätthanterade API:er har Socket.IO visat sig vara ett bibliotek för kommunikation i realtid mellan klienter och en server. Men Socket.IO användare rapporterar ofta problem med att skala Socket.IO-anslutningar. Med Web PubSub för Socket.IO behöver utvecklare inte längre oroa sig för att hantera beständiga anslutningar.

Översikt

Den här artikeln visar hur du skapar en app som gör att en kodare kan strömma kodningsaktiviteter till en målgrupp. Du skapar det här programmet med hjälp av:

  • Monaco Editor, kodredigeraren som driver Visual Studio Code.
  • Express, ett Node.js-webbramverk.
  • API:er som Socket.IO-biblioteket tillhandahåller för kommunikation i realtid.
  • Värd Socket.IO anslutningar som använder Web PubSub för Socket.IO.

Den färdiga appen

Med den färdiga appen kan användaren av en kodredigerare dela en webblänk genom vilken personer kan titta på skrivningen.

Screenshot of the finished code-streaming app.

För att hålla procedurerna fokuserade och lättsmälta på cirka 15 minuter definierar den här artikeln två användarroller och vad de kan göra i redigeraren:

  • En författare som kan skriva in onlineredigeraren och innehållet strömmas
  • Tittare som får innehåll i realtid som skrivs av författaren och inte kan redigera innehållet

Arkitektur

Objekt Syfte Förmåner
Socket.IO bibliotek Tillhandahåller en mekanism för dubbelriktat datautbyte med låg svarstid mellan serverdelsprogrammet och klienterna Lätt att använda API:er som täcker de flesta kommunikationsscenarier i realtid
Web PubSub för Socket.IO Är värd för WebSocket- eller avsökningsbaserade beständiga anslutningar med Socket.IO klienter Stöd för 100 000 samtidiga anslutningar; förenklad programarkitektur

Diagram that shows how the Web PubSub for Socket.IO service connects clients with a server.

Förutsättningar

Om du vill följa alla steg i den här artikeln behöver du:

Skapa en Web PubSub för Socket.IO resurs

Använd Azure CLI för att skapa resursen:

az webpubsub create -n <resource-name> \
                    -l <resource-location> \
                    -g <resource-group> \
                    --kind SocketIO \
                    --sku Free_F1

Skaffa en anslutningssträng

Med en anslutningssträng kan du ansluta till Web PubSub för Socket.IO.

Kör följande kommandon. Behåll de returnerade anslutningssträng någonstans, eftersom du behöver det när du kör programmet senare i den här artikeln.

az webpubsub key show -n <resource-name> \ 
                      -g <resource-group> \ 
                      --query primaryKey \
                      -o tsv

Skriva programmets kod på serversidan

Börja skriva programmets kod genom att arbeta på serversidan.

Skapa en HTTP-server

  1. Skapa ett Node.js-projekt:

    mkdir codestream
    cd codestream
    npm init
    
  2. Installera server-SDK och Express:

    npm install @azure/web-pubsub-socket.io
    npm install express
    
  3. Importera nödvändiga paket och skapa en HTTP-server för att hantera statiska filer:

    /*server.js*/
    
    // Import required packages
    const express = require('express');
    const path = require('path');
    
    // Create an HTTP server based on Express
    const app = express();
    const server = require('http').createServer(app);
    
    app.use(express.static(path.join(__dirname, 'public')));
    
  4. Definiera en slutpunkt med namnet /negotiate. En skrivarklient träffar den här slutpunkten först. Den här slutpunkten returnerar ett HTTP-svar. Svaret innehåller en slutpunkt som klienten ska använda för att upprätta en beständig anslutning. Den returnerar också ett room värde som klienten är tilldelad till.

    /*server.js*/
    app.get('/negotiate', async (req, res) => {
        res.json({
            url: endpoint
            room_id: Math.random().toString(36).slice(2, 7),
        });
    });
    
    // Make the Socket.IO server listen on port 3000
    io.httpServer.listen(3000, () => {
        console.log('Visit http://localhost:%d', 3000);
    });
    

Skapa Web PubSub för Socket.IO-servern

  1. Importera Web PubSub för Socket.IO SDK och definiera alternativ:

    /*server.js*/
    const { useAzureSocketIO } = require("@azure/web-pubsub-socket.io");
    
    const wpsOptions = {
        hub: "codestream",
        connectionString: process.argv[2]
    }
    
  2. Skapa en Web PubSub för Socket.IO server:

    /*server.js*/
    
    const io = require("socket.io")();
    useAzureSocketIO(io, wpsOptions);
    

De två stegen skiljer sig något från hur du normalt skulle skapa en Socket.IO server, enligt beskrivningen i den här Socket.IO dokumentationen. Med de här två stegen kan koden på serversidan avlasta hanteringen av beständiga anslutningar till en Azure-tjänst. Med hjälp av en Azure-tjänst fungerar programservern endast som en enkel HTTP-server.

Implementera affärslogik

Nu när du har skapat en Socket.IO server som hanteras av Web PubSub kan du definiera hur klienterna och servern kommunicerar med hjälp av Socket.IO:s API:er. Den här processen kallas för implementering av affärslogik.

  1. När en klient har anslutits meddelar programservern klienten att den är inloggad genom att skicka en anpassad händelse med namnet login.

    /*server.js*/
    io.on('connection', socket => {
        socket.emit("login");
    });
    
  2. Varje klient genererar två händelser som servern kan svara på: joinRoom och sendToRoom. När servern har fått det room_id värde som en klient vill ansluta till använder socket.join du från Socket.IO:s API för att ansluta målklienten till det angivna rummet.

    /*server.js*/
    socket.on('joinRoom', async (message) => {
        const room_id = message["room_id"];
        await socket.join(room_id);
    });
    
  3. När en klient har anslutits informerar servern klienten om det lyckade resultatet genom att skicka en message händelse. När klienten tar emot en message händelse med en typ av ackJoinRoomkan klienten be servern att skicka det senaste redigeringstillståndet.

    /*server.js*/
    socket.on('joinRoom', async (message) => {
        // ...
        socket.emit("message", {
            type: "ackJoinRoom", 
            success: true 
        })
    });
    
    /*client.js*/
    socket.on("message", (message) => {
        let data = message;
        if (data.type === 'ackJoinRoom' && data.success) {
            sendToRoom(socket, `${room_id}-control`, { data: 'sync'});
        }
        // ... 
    });
    
  4. När en klient skickar en sendToRoom händelse till servern sänder servern ändringarna i kodredigerarens tillstånd till det angivna rummet. Alla klienter i rummet kan nu få den senaste uppdateringen.

    socket.on('sendToRoom', (message) => {
        const room_id = message["room_id"]
        const data = message["data"]
    
        socket.broadcast.to(room_id).emit("message", {
            type: "editorMessage",
            data: data
        });
    });
    

Skriva programmets kod på klientsidan

Nu när procedurerna på serversidan är klara kan du arbeta på klientsidan.

Inledande installation

Du måste skapa en Socket.IO-klient för att kommunicera med servern. Frågan är vilken server klienten ska upprätta en beständig anslutning till. Eftersom du använder Web PubSub för Socket.IO är servern en Azure-tjänst. Kom ihåg att du har definierat en /negotiate-väg för att betjäna klienter en slutpunkt till Web PubSub för Socket.IO.

/*client.js*/

async function initialize(url) {
    let data = await fetch(url).json()

    updateStreamId(data.room_id);

    let editor = createEditor(...); // Create an editor component

    var socket = io(data.url, {
        path: "/clients/socketio/hubs/codestream",
    });

    return [socket, editor, data.room_id];
}

Funktionen initialize(url) organiserar några konfigurationsåtgärder tillsammans:

  • Hämtar slutpunkten till en Azure-tjänst från HTTP-servern
  • Skapar en Monaco Editor-instans
  • Upprättar en beständig anslutning med Web PubSub för Socket.IO

Skrivarklient

Som tidigare nämnts har du två användarroller på klientsidan: författare och visningsprogram. Allt som skrivarna skriver strömmas till visningsprogrammets skärm.

  1. Hämta slutpunkten till Web PubSub för Socket.IO och room_id värdet:

    /*client.js*/
    
    let [socket, editor, room_id] = await initialize('/negotiate');
    
  2. När skrivarklienten är ansluten till servern skickar servern en login händelse till skrivaren. Skrivaren kan svara genom att be servern att ansluta sig till ett angivet rum. Var 200:e millisekunder skickar skrivarklienten det senaste redigeringstillståndet till rummet. En funktion med namnet flush organiserar den sändande logiken.

    /*client.js*/
    
    socket.on("login", () => {
        updateStatus('Connected');
        joinRoom(socket, `${room_id}`);
        setInterval(() => flush(), 200);
        // Update editor content
        // ...
    });
    
  3. Om en skrivare inte gör några ändringar, flush() gör ingenting och returnerar helt enkelt. Annars skickas ändringarna i redigeringstillståndet till rummet.

    /*client.js*/
    
    function flush() {
        // No changes from editor need to be flushed
        if (changes.length === 0) return;
    
        // Broadcast the changes made to editor content
        sendToRoom(socket, room_id, {
            type: 'delta',
            changes: changes
            version: version++,
        });
    
        changes = [];
        content = editor.getValue();
    }
    
  4. När en ny visningsprogramklient är ansluten måste visningsprogrammet få det senaste fullständiga tillståndet för redigeraren. För att uppnå detta skickas ett meddelande som innehåller sync data till skrivarklienten. Meddelandet ber skrivarklienten att skicka det fullständiga redigeringstillståndet.

    /*client.js*/
    
    socket.on("message", (message) => {
        let data = message.data;
        if (data.data === 'sync') {
            // Broadcast the full content of the editor to the room
            sendToRoom(socket, room_id, {
                type: 'full',
                content: content
                version: version,
            });
        }
    });
    

Viewer-klient

  1. Precis som författarklienten skapar visningsklienten sin Socket.IO-klient via initialize(). När visningsprogrammets klient är ansluten och tar emot en login händelse från servern uppmanas servern att ansluta sig till det angivna rummet. Frågan room_id anger rummet.

    /*client.js*/
    
    let [socket, editor] = await initialize(`/register?room_id=${room_id}`)
    socket.on("login", () => {
        updateStatus('Connected');
        joinRoom(socket, `${room_id}`);
    });
    
  2. När en visningsklient tar emot en message händelse från servern och datatypen är ackJoinRoomber visningsprogrammet klienten i rummet att skicka det fullständiga redigeringstillståndet.

    /*client.js*/
    
    socket.on("message", (message) => {
        let data = message;
        // Ensures the viewer client is connected
        if (data.type === 'ackJoinRoom' && data.success) { 
            sendToRoom(socket, `${id}`, { data: 'sync'});
        } 
        else //...
    });
    
  3. Om datatypen är editorMessageuppdaterar visningsprogrammet redigeraren enligt dess faktiska innehåll.

    /*client.js*/
    
    socket.on("message", (message) => {
        ...
        else 
            if (data.type === 'editorMessage') {
            switch (data.data.type) {
                case 'delta':
                    // ... Let editor component update its status
                    break;
                case 'full':
                    // ... Let editor component update its status
                    break;
            }
        }
    });
    
  4. Implementera joinRoom() och sendToRoom() med hjälp av Socket.IO:s API:er:

    /*client.js*/
    
    function joinRoom(socket, room_id) {
        socket.emit("joinRoom", {
            room_id: room_id,
        });
    }
    
    function sendToRoom(socket, room_id, data) {
        socket.emit("sendToRoom", {
            room_id: room_id,
            data: data
        });
    }
    

Kör appen

Leta upp lagringsplatsen

Föregående avsnitt beskriver kärnlogik som rör synkronisering av redigeringstillståndet mellan visningsprogram och skrivare. Du hittar den fullständiga koden i exempellagringsplatsen.

Klona lagringsplatsen

Du kan klona lagringsplatsen och köra npm install för att installera projektberoenden.

Starta servern

node server.js <web-pubsub-connection-string>

Det här är den anslutningssträng som du fick i ett tidigare steg.

Spela med kodredigeraren i realtid

Öppna http://localhost:3000 på en webbläsarflik. Öppna en annan flik med url:en som visas på den första webbsidan.

Om du skriver kod på den första fliken bör du se hur du skriver i realtid på den andra fliken. Web PubSub för Socket.IO underlättar meddelandeöverföring i molnet. Servern express hanterar bara den statiska index.html filen och /negotiate slutpunkten.