スキップしてメイン コンテンツに移動

[Nest.js] Use WebSocket with ws

Intro

Until last time, I had used node-web-rtc to try WebRTC.
But because the example was a little complicated for I understood the core functions of using WebRTC.

So I look for other frameworks or libraries.

PeerJS is a famous library for WebRTC.
peers/peerjs: Peer-to-peer data in the browser. - GitHub
peers/peerjs-server: Server for PeerJS - GitHub
PeerJS - Simple peer-to-peer with WebRTC

A problem is I don't know how to integrate to the Nest.js project.
I couldn't find examples.

So I don't choose at least this time.

What shall I choose?

According MDN, WebRTC doesn't specify strictly what technology is used on server application for connecting two devices.
Signaling and video calling - Web APIs | MDN

But in many examples include MDN's one use WebSocket.
samples-server/s/webrtc-from-chat at master · mdn/samples-server · GitHub

So I try WebSocket in the Nest.js project.

Use WebSocket in a Nest.js project

Nest.js has a function for using WebSocket.
It can use socket.io and ws.

Because its last update date is more recently, and it is more simpler than socket.io, I choose ws this time.
websockets/ws: Simple to use, blazing fast and thoroughly tested WebSocket client and server for Node.js - GitHub
ws/ws.md at master · websockets/ws · GitHub
Adapter - Gateways | NestJS - A progressive Node.js framework

Install

npm install --save @nestjs/websockets @nestjs/platform-ws
npm install --save-dev @types/ws

Add gateway

To route WebSocket accesses, I add gateway what named "events".
Gateways | NestJS - A progressive Node.js framework
nest g gateway events

It creates two files(events.gateway.ts, events.gateway.spec.ts).
In events.gateway.ts has WebSocket by default.

events.gateway.ts


import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets';

@WebSocketGateway()
export class EventsGateway {
  @SubscribeMessage('message')
  handleMessage(client: any, payload: any): string {
    return 'Hello world!';
  }
}


There are samples for using WebSocket with socket.io and ws.
According to the sample of ws, I change main.ts and events.gateway.ts.
nest/sample/16-gateways-ws at master · nestjs/nest · GitHub

events.gateway.ts


import { SubscribeMessage, WebSocketGateway, WebSocketServer, WsResponse } from '@nestjs/websockets';
import { Server } from 'ws';
import { Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';

@WebSocketGateway()
export class EventsGateway {
  @WebSocketServer()
  server: Server;
  
  @SubscribeMessage('message')
  handleMessage(client: any, payload: any): Observable<WsResponse<number>> {
    return from([1, 2, 3]).pipe(map(item => ({ event: 'events', data: item })));
  }
}

main.ts


import { NestFactory } from '@nestjs/core';
import { WsAdapter } from '@nestjs/platform-ws';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useWebSocketAdapter(new WsAdapter(app));
  await app.listen(3000);
}
bootstrap();

Adapter

Because I forgot adding adapter into main.ts, an error occured like below.
 No driver (WebSockets) has been selected. In order to take advantage of the default driver, please, ensure to install the "@nestjs/platform-socket.io" package ($ npm install @nestjs/platform-socket.io).

Client

For Firefox and Chrome, I don't need install anything to use WebSocket.

rtc-sample.ts


export class RtcSample {
    public tryWebSocket(){
        // Replace XXX.XXX.XXX.XXX to server PC's IP address
        const ws = new WebSocket('ws://XXX.XXX.XXX.XXX:3000');
        ws.onopen = () => {
            ws.send(JSON.stringify({
                event: 'message',
                data: 'test',
              }));
        };
        ws.onmessage = data => {
            console.log(data);
        };
    }
...
}

Because I want to access from my phone, so I use IP address to create WebSocket instance.

When I start, I can get three messages.
MessageEvent {isTrusted: true, data: "{"event":"events","data":1}", origin: "ws://XXX.XXX.XXX.XXX:3000", lastEventId: "", source: null, …}

MessageEvent {isTrusted: true, data: "{"event":"events","data":2}", origin: "ws://XXX.XXX.XXX.XXX:3000", lastEventId: "", source: null, …}

MessageEvent {isTrusted: true, data: "{"event":"events","data":3}", origin: "ws://XXX.XXX.XXX.XXX:3000", lastEventId: "", source: null, …}

Send messages to connected clients

A problem is the message is only sent to client who accessed and sent message.
Though two clients(ex. clientA and clientB) have connected the server, when clientA sends a message and only clientA receives a message from server.

How to get connected clients?
The server instance has them.

events.gateway.ts


import { SubscribeMessage, WebSocketGateway, WebSocketServer, WsResponse } from '@nestjs/websockets';
import { Server } from 'ws';
import { Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';

@WebSocketGateway()
export class EventsGateway {
  @WebSocketServer()
  server: Server;
  
  @SubscribeMessage('message')
  handleMessage(client: any, payload: any): Observable<WsResponse<number>> {
    console.log(this.server.clients.size);

    const sendData = { event: 'message', data: 'hello world!!' };
    this.server.clients.forEach(s => {
      s.send(JSON.stringify(sendData));
    });
    return from([1, 2, 3]).pipe(map(item => ({ event: 'events', data: item })));
  }
}

"s" is WebSocket.
Though I can set "sendData" as the argument of s.send() directly, it is caused a runtime exception.
So I convert to JSON string.

This code sends messages to all clients.
If I want to only send to another client, I can compare with "client" of handleMessage's argument.

events.gateway.ts


...
@WebSocketGateway()
export class EventsGateway {
...
  @SubscribeMessage('message')
  handleMessage(client: any, payload: any): Observable<WsResponse<number>> {
    console.log(this.server.clients.size);

    const sendData = { event: 'message', data: 'hello world!!' };
    this.server.clients.forEach(s => {
      if (s == client) {
        return;
      }
      s.send(JSON.stringify(sendData));
    });
    return from([1, 2, 3]).pipe(map(item => ({ event: 'events', data: item })));
  }
}

References

The WebSocket API (WebSockets) - Web APIs | MDN
Gateways | NestJS - A progressive Node.js framework
websockets/ws: Simple to use, blazing fast and thoroughly tested WebSocket client and server for Node.js - GitHub
WebRTC API - Web APIs | MDN

コメント

このブログの人気の投稿

[Nest.js] Show static files

Intro I wanted to use Nest.js and WebRTC(node-webrtc). NestJS - A progressive Node.js framework Documentation | NestJS - A progressive Node.js framework And because I wanted to try with simple page(not use JavaScript frameworks), I added static HTML, CSS, JavaScript into a Nest.js project. Prepare Install First, I installed @nestjs/cli. First steps | NestJS - A progressive Node.js framework As same as last time , I couldn't do global install because I had used Volta. But I could installed by volta. volta install @nestjs/cli Create project nest new nest-web-rtc-sample volta pin node@12 Run npm start After doing "npm start", I could getting "Hello World!" from http://localhost:3000. Add static files I could add static files by two ways. @nestjs/serve-static First one of them was using "serve-static". Serve Static | NestJS - A progressive Node.js framework npm install --save @nestjs/serve-static And I needed adding a module into app.modu

[Angular][ASP.NET Core] Upload chunked files

Intro I wanted to send files to Web application (made by ASP.NET Core). If the file size had been small, I didn't need do any special things. But when I tried to send a large file, the error was occurred by ASP.NET Core's limitation. Though I could change the settings, but I didn't want to do that, because I hadn't known the file sizes what would been actually using. So I splitted the data into chunks first, and sent them. After receiving all chunks, I merged them into one file. There might be some libraries or APIs (ex. Stream API) what did them automatically, but I couldn't find them. What I did [ASP.NET Core] Make CORS enabled [Angular] Split a large file into chunks [Angular][ASP.NET Core] Send and receive data as form data [ASP.NET Core] Merge chunks into one file [ASP.NET Core] Make CORS enabled Because the client side application(Angular) and the server side application(ASP.NET Core) had been separated, I had to make CORS(Cross-Origin Requests)