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

[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

コメント

このブログの人気の投稿

[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) ...

[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] Sending file with Observable and showing loading screen

Intro When I tried sending file data on last time, I had confused with "promise.then", "async/await" and "Observable". [Angular][ASP.NET Core] Upload chunked files So I wanted to distinct them, and this time, I tried to use "Observable" because HttpClient return Observable<any>. Call observables in order I sended file in these steps. Read file by FileReader Create directory for saving chunks send and saving chunks merge chunks to one file and delete chunks Each steps used the former steps result. So I could write by Promise.then like below. this.executeStep1() // return Promise<UploadResult> .then(result => this.executeStep2(result)) // return Promise<UploadResult> .then(result => this.executeStep3(result)) // return Promise<UploadResult> .catch(reason => console.log(reason)); Result I could write with pipe & flatMap. file-uploader.service.ts public upload(file: File): Observable<U...