# How Socket.IO Token-Based Communication Enables Real-Time Progress Tracking

> Learn how Socket.IO token-based communication tracks download progress in real-time. Discover how unique tokens prevent broadcast collisions for granular updates from your server.

- Repository: [Ahmed Ibrahim/Website-downloader](https://github.com/AhmadIbrahiim/Website-downloader)
- Tags: how-to-guide
- Published: 2026-07-08

---

**Socket.IO token-based communication creates request-scoped pub/sub channels by using unique tokens as dynamic event names, allowing the server to push granular download progress from child processes to specific browser clients without broadcast collisions.**

The AhmadIbrahiim/Website-downloader repository demonstrates how Socket.IO token-based communication solves the challenge of tracking long-running server operations in real time. By treating unique tokens as isolated messaging channels, the application streams live progress updates from `wget` and archiving processes directly to the client that initiated the download, even when multiple users run parallel sessions.

## Architecture of Token-Based Communication

### Connection Handling and Request Registration

When a browser connects, the server registers event listeners in [`socket/socket.js`](https://github.com/AhmadIbrahiim/Website-downloader/blob/main/socket/socket.js). The `request` handler captures the unique token (typically a UUID) sent by the client and initiates the download workflow.

```javascript
// socket/socket.js
module.exports = (io) => {
  io.on('connection', (socket) => {
    socket.on('request', (data) => {
      console.log('Request connection received %s', data.token);
      wget(io, data);               // pass io so wget can emit progress
    });
    
    socket.on('disconnect', () => {
      // cleanup if needed
    });
  });
};

```

The server passes the Socket.IO instance (`io`) to the `wget` module so that progress events can be emitted from within the child process handlers.

### Token as Dynamic Event Namespace

Unlike static Socket.IO rooms, the token serves as a dynamic event identifier. The server uses `data.token` as the event name itself, creating an isolated communication channel for each download operation. This pattern ensures that messages emitted with `io.emit(data.token, ...)` reach only the client that generated that specific token.

## Implementing Progress Tracking with Child Processes

### Spawning wget and Capturing stderr

In [`wget/index.js`](https://github.com/AhmadIbrahiim/Website-downloader/blob/main/wget/index.js), the server spawns a child `wget` process and monitors its `stderr` stream for progress data. Each time the stream outputs a new chunk, the server immediately forwards it to the client using the token as the event key.

```javascript
// wget/index.js (excerpt)
child.stderr.on('data', (response) => {
  const responseText = response.toString();
  io.emit(data.token, { progress: responseText });
});

```

This line (line 33 in the source) is the core mechanism: `io.emit(data.token, { progress: responseText })` pushes real-time download percentages and status messages to the browser.

### State Transition Notifications

When the `wget` process closes, the server notifies the client of state changes before triggering the archiving step. This provides visual feedback when the download transitions from fetching to processing.

```javascript
// wget/index.js (lines 44-45)
child.stderr.on('close', () => {
  io.emit(data.token, { progress: 'Converting' });
  archiver(websiteFolder, io, data);
});

```

### Archiving and Completion Events

The [`archiver/index.js`](https://github.com/AhmadIbrahiim/Website-downloader/blob/main/archiver/index.js) module emits the final event when the ZIP file is ready. This message includes the filename, allowing the client to construct a download link.

```javascript
// archiver/index.js (line 17)
output.on('close', () => {
  io.emit(data.token, { progress: 'Completed', file });
});

```

## Client-Side Token Subscription

The browser generates a unique token, emits it with the `request` event, and listens exclusively for events matching that token value. This creates a private subscription channel without requiring explicit Socket.IO room management.

```html
<script src="/socket.io/socket.io.js"></script>
<script>
  const socket = io();
  const token = crypto.randomUUID(); // unique per download
  const website = document.getElementById('url').value;

  // start download
  socket.emit('request', { token, website });

  // listen for progress updates
  socket.on(token, (msg) => {
    const log = document.getElementById('log');
    log.textContent += `\n${msg.progress}`;
    if (msg.file) {
      const link = document.createElement('a');
      link.href = `/public/sites/${msg.file}.zip`;
      link.textContent = 'Download ZIP';
      log.appendChild(link);
    }
  });
</script>

```

Because the token is generated client-side and passed back as the event name, the front-end receives only the updates relevant to its specific request, even when multiple downloads run concurrently on the server.

## Summary

- **Socket.IO token-based communication** uses unique tokens as dynamic event names to create isolated messaging channels per download request.
- In [`socket/socket.js`](https://github.com/AhmadIbrahiim/Website-downloader/blob/main/socket/socket.js), the server captures the token from the `request` event and passes it to the `wget` module for scoped emissions.
- The [`wget/index.js`](https://github.com/AhmadIbrahiim/Website-downloader/blob/main/wget/index.js) file streams `stderr` output directly to the client via `io.emit(data.token, { progress: responseText })`, enabling real-time progress visualization.
- State transitions ("Converting", "Completed") and final file delivery follow the same token-scoped pattern in [`archiver/index.js`](https://github.com/AhmadIbrahiim/Website-downloader/blob/main/archiver/index.js).
- This architecture supports multiple concurrent downloads without message leakage between clients, as each browser subscribes only to its specific token events.

## Frequently Asked Questions

### Why use tokens instead of Socket.IO rooms for progress tracking?

Using tokens as dynamic event names is lighter than managing Socket.IO rooms because it eliminates the need to call `socket.join()` and `socket.to()`. According to the AhmadIbrahiim/Website-downloader source code, the server simply emits to `data.token` and the client listens for that specific string, creating a de facto private channel with less overhead.

### How does the server prevent progress updates from leaking to other clients?

The server prevents leakage by using the unique token as the event name in `io.emit(data.token, payload)`. Since other clients are not listening for that specific token string, they never receive the message. This is implemented in [`wget/index.js`](https://github.com/AhmadIbrahiim/Website-downloader/blob/main/wget/index.js) at line 33 and [`archiver/index.js`](https://github.com/AhmadIbrahiim/Website-downloader/blob/main/archiver/index.js) at line 17, ensuring each download operation remains isolated.

### What happens if the client disconnects during the download?

If the client disconnects, the Socket.IO `disconnect` event fires in [`socket/socket.js`](https://github.com/AhmadIbrahiim/Website-downloader/blob/main/socket/socket.js), but the `wget` and archiving processes continue running on the server. The server does not implement explicit cleanup logic in the provided code, meaning the child processes will complete their work and emit to a now-empty channel. The downloaded files remain on the server until explicitly purged.

### Can this pattern handle multiple simultaneous downloads from the same browser?

Yes. Each download generates a distinct token (e.g., via `crypto.randomUUID()`), and the client calls `socket.on(token, handler)` for each unique token. This allows the same browser tab to track multiple concurrent downloads by maintaining separate listeners for each token, as demonstrated by the dynamic event subscription pattern in the client-side example.