将 NestJS 连接到 websocket 服务器

Posted

技术标签:

【中文标题】将 NestJS 连接到 websocket 服务器【英文标题】:Connect NestJS to a websocket server 【发布时间】:2020-08-22 10:07:48 【问题描述】:

如何将 NestJS 用作 websocket 客户端?我想使用 NestJS 作为客户端连接到远程 websocket 服务器,但我没有在框架中找到有关此实现的任何信息。

【问题讨论】:

【参考方案1】:

由于 Nestjs 只是 Nodejs 的一个框架,所以需要找一个支持 Websocket 的 NPM 包。例如,我使用ws@types/ws 类型定义,并创建一个Websocket 客户端作为Nestjs 服务类:

// socket-client.ts
import  Injectable  from "@nestjs/common";
import * as WebSocket from "ws";

@Injectable()
export class WSService 
    // wss://echo.websocket.org is a test websocket server
    private ws = new WebSocket("wss://echo.websocket.org");

    constructor() 
        this.ws.on("open", () => 
            this.ws.send(Math.random())
        );

        this.ws.on("message", function(message) 
            console.log(message);
        );
    

    send(data: any) 
        this.ws.send(data);
    

    onMessage(handler: Function) 
        // ...
    

    // ...


// app.module.ts
import  Module  from "@nestjs/common";
import  WSService  from "./socket-client";

@Module(
    providers: [WSService]
)
export class AppModule 

【讨论】:

这里不能使用内置的 websocket 功能吗? docs.nestjs.com/websockets/gateways【参考方案2】:

我用另一种方式尝试。我用socket.io-client 编写了一个适配器。然后通过方法useWebSocketAdapter 在boostrap 中使用这个适配器。之后我可以在网关中编写处理 websocket 事件,就像使用套接字服务器的方式一样(使用装饰器 @SubscribeMessage

我的适配器文件

import  WebSocketAdapter, INestApplicationContext  from '@nestjs/common';
import  MessageMappingProperties  from '@nestjs/websockets'

import * as SocketIoClient from 'socket.io-client';
import  isFunction, isNil  from '@nestjs/common/utils/shared.utils';
import  fromEvent, Observable  from 'rxjs';
import  filter, first, map, mergeMap, share, takeUntil  from 'rxjs/operators';

export class IoClientAdapter implements WebSocketAdapter 
    private io;
    constructor(private app: INestApplicationContext) 

    

    create(port: number, options?: SocketIOClient.ConnectOpts) 
        const client = SocketIoClient("http://localhost:3000" , options || )
        this.io = client;
        return client;
    

    bindClientConnect(server: SocketIOClient.Socket, callback: Function) 
        this.io.on('connect', callback);
    

    bindClientDisconnect(client: SocketIOClient.Socket, callback: Function) 
        console.log("it disconnect")
        //client.on('disconnect', callback);
    

    public bindMessageHandlers(
        client: any,
        handlers: MessageMappingProperties[],
        transform: (data: any) => Observable<any>,
    ) 
        const disconnect$ = fromEvent(this.io, 'disconnect').pipe(
            share(),
            first(),
        );

        handlers.forEach(( message, callback ) => 
            const source$ = fromEvent(this.io, message).pipe(
                mergeMap((payload: any) => 
                    const  data, ack  = this.mapPayload(payload);
                    return transform(callback(data, ack)).pipe(
                        filter((response: any) => !isNil(response)),
                        map((response: any) => [response, ack]),
                    );
                ),
                takeUntil(disconnect$),
            );
            source$.subscribe(([response, ack]) => 
                if (response.event) 
                    return client.emit(response.event, response.data);
                
                isFunction(ack) && ack(response);
            );
        );
    

    public mapPayload(payload: any):  data: any; ack?: Function  
        if (!Array.isArray(payload)) 
            return  data: payload ;
        
        const lastElement = payload[payload.length - 1];
        const isAck = isFunction(lastElement);
        if (isAck) 
            const size = payload.length - 1;
            return 
                data: size === 1 ? payload[0] : payload.slice(0, size),
                ack: lastElement,
            ;
        
        return  data: payload ;
    

    close(server: SocketIOClient.Socket) 
        this.io.close()
    


main.js

import  NestFactory  from '@nestjs/core';
import  AppModule  from './app.module';
import IoClientAdapter from './adapters/ioclient.adapter'

async function bootstrap() 
  const app = await NestFactory.create(AppModule);
  app.useWebSocketAdapter(new IoClientAdapter(app))
  await app.listen(3006);
  console.log(`Application is running on: $await app.getUrl()`);

bootstrap();

然后是网关

import 
  MessageBody,
  SubscribeMessage,
  WebSocketGateway,
  WebSocketServer,
  WsResponse,
 from '@nestjs/websockets';
import  from, Observable  from 'rxjs';
import  map  from 'rxjs/operators';
import  Server  from 'socket.io';

@WebSocketGateway()
export class EventsGateway 
  @WebSocketServer()
  server: Server;

  @SubscribeMessage('hello')
  async identity(@MessageBody() data: number): Promise<number> 
      console.log(data)
    return data;
  

这是一个技巧,但看起来很酷。消息处理程序可以写得更像nestjs风格。

【讨论】:

以上是关于将 NestJS 连接到 websocket 服务器的主要内容,如果未能解决你的问题,请参考以下文章

是否可以将JS WebSocket连接到Python服务器?

我可以将 websocket 连接到非 websocket 服务器吗

将 docker 容器中的 websocket 客户端连接到主机中的 websocket 服务器

如何将android设备连接到Spring websocket服务器

将 JavaScript WebSocket 连接到 C winsock

Dockerizing NestJS APP 和 MongoDB “首次连接时无法连接到服务器 [localhost:27017]”