Web Socket

WebSockets allow real-time, two-way communication between the client and server

Using WebSockets with the WebApp Package

Home documentation

Overview

WebSockets allow real-time, two-way communication between the client and server. This guide explains how to configure a WebSocket server with the WebApp package in Dart, set up WebSocket routes, and connect a frontend to communicate with the WebSocket server.

Server-Side Configuration

1. Setting Up the Server

To use WebSockets with the WebApp package, configure the server and WebSocket manager as follows:

import 'package:webapp/wa_server.dart';
import 'route/socket_route.dart';

WaConfigs configs = WaConfigs(
  widgetsPath: pathTo("./example/widgets"),
  widgetsType: 'j2.html',
  languagePath: pathTo('./example/languages'),
  port: 8085,
  dbConfig: WaDBConfig(enable: false),
  publicDir: pathTo('./example/public'),
);

WaServer server = WaServer(configs: configs);

final socketManager = SocketManager(
  server,
  event: SocketEvent(
    onConnect: (socket) {
      server.socketManager?.sendToAll(
        "New user connected! count: ${server.socketManager?.countClients}",
        path: "output",
      );
      socket.send(
        {'message': 'Success connect to socket!'},
        path: 'connected',
      );
    },
    onMessage: (socket, data) {},
    onDisconnect: (socket) {
      var count = server.socketManager?.countClients ?? 0;
      server.socketManager?.sendToAll(
        "User disconnected! count: ${count - 1}",
        path: "output",
      );
    },
  ),
  routes: getSocketRoute(),
);

Explanation:

  • WaServer: Creates a new server instance with the given configurations.
  • SocketManager: Manages WebSocket connections.
  • SocketEvent: Handles events such as connection, message receipt, and disconnection.
  • server.socketManager?.sendToAll: Sends a message to all connected clients.
  • socket.send: Sends a message to the individual socket connection.

2. Configuring WebSocket Routes

Define WebSocket routes and their corresponding actions:

import 'package:webapp/wa_server.dart';

Map<String, SocketEvent> getSocketRoute() {
  var count = 0;
  return {
    'test': SocketEvent(
      onMessage: (socket, data) {
        socket.send([socket.rq.headers], path: 'test');
      },
    ),
    'close': SocketEvent(
      onMessage: (socket, data) {
        socket.close();
      },
    ),
    'time': SocketEvent(
      onMessage: (socket, data) {
        socket.send(DateTime.now().toString(), path: 'output');
      },
    ),
    'fa': SocketEvent(
      onMessage: (socket, data) async {
        for (var i = 0; i < 10; i++) {
          await Future.delayed(Duration(seconds: 1), () {
            count++;
            socket.send('======= $count ====== ', path: 'output');
          });
        }
      },
    ),
    'clients': SocketEvent(
      onMessage: (socket, data) {
        var clients = server.socketManager?.getAllClientsKeys();
        socket.send(clients, path: 'clients');
      },
    ),
    'toclient': SocketEvent(
      onMessage: (socket, data) {
        var id = data['data']?['id'] ?? '';
        var message = data['data']?['message'] ??
            'You receive a new message from another client';
        var client = socket.manager.session.getClient(id);
        client?.send(
          "Client ${socket.id}: $message",
          path: 'output',
        );
      },
    ),
  };
}

Explanation:

  • 'test': Sends headers of the socket request.
  • 'close': Closes the WebSocket connection.
  • 'time': Sends the current time.
  • 'fa': Sends incremented count values every second.
  • 'clients': Sends a list of all connected clients.
  • 'toclient': Sends a message to a specific client identified by ID.

Client-Side Configuration

1. Establishing WebSocket Connection

Use JavaScript to connect to the WebSocket server and handle events:

var socketOutput = 0;
var socketEvents = {
    connected: function (data) {
        this.output({ data: 'Web Socket connected' });
        console.log('Web Socket connected');
    },

    close: function (e) {
        this.output({ data: 'Web Socket closed' });
        console.log('Web Socket closed');
    },

    output: function (data) {
        socketOutput++;
        if (document.getElementById('socket-output'))
            document.getElementById('socket-output').innerHTML = `${socketOutput}.  ${data.data}\n` + document.getElementById('socket-output').innerHTML;
    },

    clients: function (data) {
        if (document.getElementById('socket-output')) {
            document.getElementById('client-list').innerHTML = '';

            for (var i = 0; i < data.data.length; i++) {
                var id = data.data[i];
                var template = document.getElementById('btn-template-client').innerHTML;
                document.getElementById('socket-output').innerHTML = `Client ${i + 1}: ${id}\n` + document.getElementById('socket-output').innerHTML;
                template = template.replace('{text}', `Client ${i + 1}`);
                template = template.replace('{id}', id);
                document.getElementById('client-list').innerHTML += template;
            }

            initClientList();
        }
    },
}

var socket = new WebSocket("ws://localhost:8085/ws");
socket.onmessage = function (e) {
    var data = JSON.parse(e.data);
    console.log(data);
    if (socketEvents[data.path]) {
        socketEvents[data.path](data);
    }
};

socket.onclose = function (e) {
    socketEvents.close(e);
}

document.getElementById('btn-socket-time')?.addEventListener('click', function () {
    socket.send(JSON.stringify({ path: 'time' }));
});

document.getElementById('btn-socket-fa')?.addEventListener('click', function () {
    socket.send(JSON.stringify({ path: 'fa' }));
});

document.getElementById('btn-socket-clients')?.addEventListener('click', function () {
    socket.send(JSON.stringify({ path: 'clients' }));
});

function initClientList() {
    document.querySelectorAll('.socket-client-send')?.forEach(function (element) {
        element.addEventListener('click', function () {
            var id = this.getAttribute('data-id');
            socket.send(JSON.stringify({ path: 'toclient', data: { id, message: `Hello! How are you?` } }));
        });
    });
}

Explanation:

  • var socket: Establishes a WebSocket connection to the server.
  • socket.onmessage: Handles incoming messages from the server based on the path.
  • socket.onclose: Handles WebSocket disconnections.
  • socket.send: Sends messages to the WebSocket server.
  • initClientList: Adds event listeners to dynamically created client list elements to send messages to specific clients.

Summary

  • Server-Side: Set up WebSocket routes and manage client connections using SocketManager and SocketEvent.
  • Client-Side: Connect to the WebSocket server using JavaScript, handle incoming messages, and send messages from the client.

This setup allows for real-time communication and interaction between the server and clients, providing a robust framework for building interactive web applications.