Setting Up a Signalling Server: Client Video Streaming (Part 2 of 3)
Published: 2023-05-12 07:55:19


Welcome back to our thrilling three-part series on setting up a peer-to-peer connection for client video streaming. In [Part 1](https://www.balysnotes.com/setting-up-a-peer-to-peer-connection-video-stream), we got our hands dirty by setting up the basics of a client-side WebRTC application. Now, it's time to tackle the server-side of things. Buckle up, because we're about to dive into the fascinating world of signalling servers and WebRTC peer information exchange!

Here's the link to the [repo](https://github.com/TylerMutai/peer-to-peer-video-conferencing).

Here's a link to the [original post.](https://www.balysnotes.com/setting-up-a-signaling-server-client-video-streaming)

## Previously on "Client Video Streaming"...

In the last episode, we set up our client-side application to establish a WebRTC connection, allowing video streaming between two clients. However, our two clients were lonely and couldn't find each other in the vast internet space. This is where our signalling server comes to the rescue. It will act as a matchmaker, helping the two clients find each other and exchange the necessary information to start their video streaming relationship.

## A Signalling Server to the Rescue

Now that we've set the stage, let's get down to business. Our mission today is to set up a signalling server that will help users find one another and establish a WebRTC connection. 
As a reference, we'll be using the code snippet provided below. We'll be using TypeScript, Express, and Socket.IO to make this happen.

```typescript
import express from 'express';
import { Server as HttpServer } from 'http';
import { Server as SocketIOServer, Socket } from 'socket.io';

const app = express();
const httpServer = new HttpServer(app);
const io = new SocketIOServer(httpServer);

app.use(express.static('public'));

io.on('connection', (socket: Socket) => {
  console.log('User connected:', socket.id);

  socket.on('join-room', (roomId: string) => {
    console.log('User joined room:', roomId);
    const roomClients = io.sockets.adapter.rooms.get(roomId);

    if (!roomClients || roomClients.size === 0) {
      socket.join(roomId);
    } else {
      socket.emit('room-full', 'Room is full');
      socket.disconnect();
    }
  });

  socket.on('offer', (data: { roomId: string; offer: RTCSessionDescriptionInit }) => {
    socket.to(data.roomId).emit('offer', data.offer);
  });

  socket.on('answer', (data: { roomId: string; answer: RTCSessionDescriptionInit }) => {
    socket.to(data.roomId).emit('answer', data.answer);
  });

  socket.on('icecandidate', (data: { roomId: string; candidate: RTCIceCandidateInit }) => {
    socket.to(data.roomId).emit('icecandidate', data.candidate);
  });

  socket.on('disconnect', () => {
    console.log('User disconnected:', socket.id);
  });
});

const PORT = process.env.PORT || 3000;
httpServer.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
```

### Step 1: Setting up the server

First things first, let's set up our server using Express and Socket.IO. Our server will be listening on port 3000 (or any available port in the environment). Here's the code to get our server up and running:

```typescript
import express from 'express';
import { Server as HttpServer } from 'http';
import { Server as SocketIOServer, Socket } from 'socket.io';

const app = express();
const httpServer = new HttpServer(app);
const io = new SocketIOServer(httpServer);

const PORT = process.env.PORT || 3000;
httpServer.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
```

### Step 2: Serving static files

To serve our client-side files, we'll use the `express.static` middleware. This will enable our server to serve static files from the `public` folder:

```typescript
app.use(express.static('public'));
```

### Step 3: Handling Socket.IO connections

Now, it's time to handle the connections coming from our clients. We'll listen for the `connection` event on our Socket.IO server and print a message whenever a user connects:

```typescript
io.on('connection', (socket: Socket) => {
  console.log('User connected:', socket.id);
});
```

### Step 4: Room management and WebRTC signalling

Our server will be responsible for managing rooms and exchanging WebRTC peer information between the clients. To do this, we'll listen for various events, such as `join-room`, `offer`, `answer`, and `icecandidate`. We'll also handle the `disconnect` event to clean up after a user leaves:

```typescript
socket.on('join-room', (roomId: string) => {
  // ...
});

socket.on('offer', (data: { roomId: string; offer: RTCSessionDescriptionInit }) => {
  // ...
});

socket.on('answer', (data: { roomId: string; answer: RTCSessionDescriptionInit }) => {
  // ...
});

socket.on('icecandidate', (data: { roomId: string; candidate: RTCIceCandidateInit }) => {
  // ...
});

socket.on('disconnect', () => {
  console.log('User disconnected:', socket.id);
});
```

Within each event handler, we'll manage the rooms and forward the WebRTC information to the appropriate clients. For example, when handling the `join-room` event, we'll check if the room is full and either let the user join or disconnect them:

```typescript
socket.on('join-room', (roomId: string) => {
  console.log('User joined room:', roomId);
  const roomClients = io.sockets.adapter.rooms.get(roomId);

  if (!roomClients || roomClients.size === 0) {
    socket.join(roomId);
  } else {
    socket.emit('room-full', 'Room is full');
    socket.disconnect();
  }
});
```

And there you have it! With these event handlers in place, our signalling server is now ready to help clients exchange WebRTC peer information and establish video streaming connections.

## Coming Soon: The Final Chapter

Phew! Our signalling server is up and running, and our clients are no longer lonely. They can now find each other and start streaming video, all thanks to our trusty server-side matchmaker. But our journey doesn't end here! In the next and final part of this series, we'll put everything together and make some final adjustments to our application, ensuring that our video streams seamlessly between clients.

See you in the last part of this series!