Skip to content

WebSockets

Inhoud

De server

Om de effectieve chat functionaliteit te laten werken hebben we nood aan een server (en een databank). De server speelt namelijk een cruciale rol als het centrale punt dat alle clientverbindingen en berichtrouting beheert.

Serverfunctionaliteit

De server zal verschillende essentiële functies uitvoeren:

  1. Beheer van Clientverbindingen: De server accepteert en onderhoudt verbindingen van alle chatclients.
  2. Kamerbeheer: De chat biedt meerdere chatkamers aan voor verschillende onderwerpen. Wanneer een gebruiker verbinding maakt, zal de server een lijst met beschikbare chatkamers doorgeven.
  3. Berichtafhandeling: De server zal:
    • Berichten ontvangen van verbonden clients
    • Inkomende berichten verwerken
    • Berichten uitzenden naar alle clients binnen de specifieke chatkamer

Wat zijn websockets?

Voor de realtime communicatie gaan we gebruik maken van de WebSockets API.

HTTP vs. WebSocket

HTTP Communicatie:

  • HTTP is het standaardprotocol voor webcommunicatie tussen clients en servers
  • Het is unidirectioneel (eenrichtingscommunicatie)
  • Elke aanvraag-responscyclus is een afzonderlijke verbinding

WebSocket Communicatie:

  • WebSocket biedt full duplex (tweerichtings) communicatie
  • Zorgt voor een persistente verbinding met de server
  • Maakt realtime gegevensuitwisseling mogelijk
  • Efficiënter voor toepassingen die continue communicatie vereisen

WebSocket Architectuur

Een belangrijk concept om te begrijpen over WebSocket is dat clients niet rechtstreeks met elkaar kunnen communiceren. Alle communicatie verloopt via de server:

  1. Client stuurt een bericht naar de server
  2. Server ontvangt het bericht
  3. Server verspreidt het bericht naar de juiste clients (naar alle verbonden clients of naar specifieke clients)

Dit communicatiemodel is fundamenteel voor hoe onze chatapplicatie zal functioneren.

WebSocket Server Events

Onze WebSocket-server zal event-driven zijn en reageren op verschillende belangrijke gebeurtenissen:

  • open: Wordt geactiveerd wanneer een client verbinding maakt met de server
javascript
addEventListener(
    'open', (event) => {
    }
);
const socket = new WebSocket('ws://localhost:3000');
socket.addEventListener(
    'open', (event) => {
        socket.send('New client connected');
    }
)
  • message: Wordt geactiveerd wanneer de WebSocket gegevens ontvangt
javascript
const socket = new WebSocket('ws://localhost:3000');
socket.addEventListener(
    'message', (event) => {
        console.log('Message from the server ', event.data);
    }
)
  • error: Wordt geactiveerd wanneer er een fout optreedt (bijvoorbeeld wanneer gegevens niet kunnen worden verzonden)
javascript
const socket = new WebSocket('ws://localhost:3000');
socket.addEventListener(
    'error', (event) => {
        console.log('Connection error ', event);
    }
)
  • close: Wordt geactiveerd wanneer een WebSocket-verbinding wordt gesloten, maar sluit zelf de connectie niet.
javascript
const socket = new WebSocket('ws://localhost:3000');
socket.addEventListener(
    'close', (event) => {
        console.log('Connection has been closed');
    }
)

WebSocket methodes

Dit waren WebSocket-events waarop we kunnen luisteren en reageren. Er zijn ook instantiemethoden die beschikbaar zijn voor het WebSocket-object, waarmee we actief berichten kunnen verzenden en verbindingen kunnen beheren.

De Send Methode

De send methode wordt gebruikt om gegevens van de client naar de server te verzenden.

Ondersteunde gegevenstypen door send():

  • Platte tekst strings
  • Binaire gegevens:
    • Blob objecten - kunnen elk bestandstype vertegenwoordigen (audio, video, etc.)
    • ArrayBuffer objecten - voor verwerking van ruwe binaire gegevens
Belangrijk gedrag:
  • De methode vergroot de interne buffer met de hoeveelheid die nodig is om de gegevens te verzenden
  • Als de buffer vol raakt en gegevens niet kunnen worden verzonden, wordt de verbinding gesloten
  • Gegevens worden stilzwijgend weggegooid als de verbinding in de status CLOSED of CLOSING is
  • Er wordt een uitzondering gegenereerd als send wordt aangeroepen terwijl de verbinding in de CONNECTING-status is
javascript
// Tekstgegevens verzenden
webSocket.send("Hallo, server!");

// JSON-gegevens verzenden
webSocket.send(JSON.stringify({bericht: "Hallo", gebruiker: "Jan"}));

// Binaire gegevens verzenden
webSocket.send(blobObject);

De Close Methode

De close methode beëindigt de WebSocket-verbinding.

Basisgebruik:
javascript
webSocket.close();
Geavanceerde opties:

De close-methode kan twee optionele parameters accepteren:

  1. code - Een statuscode die aangeeft waarom de verbinding wordt gesloten
  2. reden - Een leesbare tekst die de sluiting uitlegt
javascript
// Sluiten met statuscode en reden
webSocket.close(1000, "Gebruiker heeft de chat verlaten");
Sluitingsstatuscodes:
  • 1000 - Normale sluiting (standaard als er geen code is opgegeven)
  • 1001-1015 - Gereserveerd voor specifieke sluitingsredenen
  • 3000-4999 - Beschikbaar voor aangepaste toepassingscodes

Best Practices

  • Sluit WebSocket-verbindingen altijd wanneer ze niet meer nodig zijn (bijv. wanneer een gebruiker de chat verlaat)
  • Behandel de verbindingsstatus op de juiste manier voordat je probeert gegevens te verzenden
  • Gebruik statuscodes om verschillende soorten verbindingssluitingen correct af te handelen

Voor een uitgebreide lijst van standaard sluitingscodes en hun betekenissen kan je de WebSocket API-documentatie raadplegen.

WebSocket eigenschappen

Elke WebSocket-verbinding biedt verschillende standaard eigenschappen die ons informatie geven over de verbinding en ons in staat stellen bepaalde aspecten van het gedrag ervan te beheren.

URL

Geeft de URL van de WebSocket terug die werd opgegeven in de constructor:

javascript
const socket = new WebSocket('ws://example.com/chat');
console.log(socket.url); // 'ws://example.com/chat'

protocol

Geeft het subprotocol terug dat door de server is geselecteerd uit de protocollen die aan de WebSocket-constructor zijn doorgegeven:

javascript
const socket = new WebSocket('ws://example.com/chat', ['protocol1', 'protocol2']);
console.log(socket.protocol); // Kan 'protocol1' teruggeven als de server dit heeft geaccepteerd

Als er geen protocol is opgegeven of als er geen is geselecteerd door de server, geeft deze eigenschap een lege string terug.

readyState

Geeft de huidige status van de WebSocket-verbinding terug als een numerieke waarde:

ConstanteWaardeBeschrijving
WebSocket.CONNECTING0De verbinding wordt tot stand gebracht
WebSocket.OPEN1De verbinding is tot stand gebracht en communicatie is mogelijk
WebSocket.CLOSING2De verbinding is bezig met sluiten
WebSocket.CLOSED3De verbinding is gesloten of kon niet worden geopend
javascript
if (socket.readyState === WebSocket.OPEN) {
    socket.send('Bericht kan nu worden verzonden');
}

Deze eigenschap is cruciaal voor het bepalen of operaties zoals send() veilig kunnen worden uitgevoerd. Zoals eerder vermeld, zal het proberen gegevens te verzenden terwijl de status CONNECTING is een fout veroorzaken.

bufferedAmount

Geeft het aantal bytes aan gegevens terug dat in de wachtrij staat met behulp van de send()-methode, maar nog niet naar de server is verzonden:

javascript
socket.send('Hallo');
console.log(socket.bufferedAmount); // Kan 5 (bytes) teruggeven

extensions

Geeft een string terug met eventuele extensies die de server heeft onderhandeld voor de verbinding:

javascript
console.log(socket.extensions); // Kan 'permessage-deflate' teruggeven

Extensies kunnen extra functionaliteit bieden aan het WebSocket-protocol.

binaryType

Bepaalt hoe binaire gegevens worden ontvangen via de WebSocket:

javascript
// Standaard is 'blob'
console.log(socket.binaryType); // 'blob'

// Kan worden ingesteld op 'arraybuffer'
socket.binaryType = 'arraybuffer';

Opties:

  • 'blob' (standaard) - Binaire gegevens worden teruggegeven als Blob-objecten
  • 'arraybuffer' - Binaire gegevens worden teruggegeven als ArrayBuffer-objecten

Eigenschappen in de Praktijk Gebruiken

Deze eigenschappen zijn bijzonder nuttig voor:

  • Het bewaken van de verbindingsstatus voordat operaties worden uitgevoerd
  • Het bijhouden van gegevensoverdracht
  • Het optimaliseren van prestaties door bufferhoeveelheden te controleren
  • Het instellen van geschikte gegevenstypen voor binaire overdracht

De uitgewerkte code

De server gaan jullie niet zelf moeten bouwen, maar krijgen jullie aangeleverd. De server is gebouwd met Socket.io, een krachtig framework dat zowel aan de server- als aan de clientzijde werkt.

Socket.io vermindert aanzienlijk de hoeveelheid standaardcode die nodig is in vergelijking met het direct werken met de WebSocket API. In plaats van handmatig event listeners en handlers op te zetten voor elke WebSocket-gebeurtenis, kunnen we gebruik maken van Socket.io's intuïtieve methoden die deze details voor ons afhandelen.

javascript
// Standaard WebSocket-benadering
const ws = new WebSocket('ws://example.com');
ws.addEventListener('message', (event) => {
    const data = JSON.parse(event.data);
    // Gegevens verwerken
});

// Socket.io-benadering
const socket = io('http://example.com');
socket.on('message', (data) => {
    // Gegevens verwerken - al geparsed!
});

Ingebouwde Ondersteuning voor Kamers

Socket.io biedt native ondersteuning voor chatkamers, een functie die we nodig hebben voor onze chat-applicatie. Hiermee is het mogelijk om:

  • Meerdere chatkamers te creëren
  • Gemakkelijk kamers te betreden en te verlaten
  • Berichten naar specifieke kamers te sturen
  • Deelnemers aan kamers bij te houden

De uitgewerkte server kan je hier terugvinden.