【nestjs】使用websocket协议通信

前言

nestjs特别青睐socket.io,文档中关于网关的描述都是以socket.io为例,并且不管是@nestjs/platform-socket.io还是@nestjs/platform-ws的适配器实现都是参考socket.io的,以至于想完整地使用websocket协议进行通信变得难以实现。

WsAdapter遇到的坑

想要使用ws库,需要使用@nestjs/platform-wsWsAdapter,使用方法如下:

// main.ts
// 必须在app.listen方法之前调用
 app.useWebSocketAdapter(new WsAdapter(app));
 await app.listen(3000);

然后开始踩坑。

@SubscribeMessage订阅事件装饰器

在websocket协议里,是没有订阅事件的概念的,这是socket.io里的概念。这里我的解决办法是写死事件名,例如@SubscribeMessage('message')

客户端调用send的方法,服务器无法收到

排除客户端出现问题的可能,测试了一些特定格式的JSON数据是可以的,例如:

{
	"event": "message",
	"data": {
		"test": 123
	}
}

翻了源码,发现是需要按照上述格式的JSON的数据解析的。

// ws-adapter.ts
 const message = JSON.parse(buffer.data);
 const messageHandler = handlers.find(
   handler => handler.message === message.event,
 );
 const { callback } = messageHandler;
 return transform(callback(message.data));

解决方案

@nestjs/platform-ws适配器对传输数据进行了封装,因此我们只要自己写一个适配器就可以解决这个问题。
源码如下:

// src/adapters/ws.adapter.ts
import { WsAdapter as OriginWsAdapter } from '@nestjs/platform-ws';
import { MessageMappingProperties } from '@nestjs/websockets';
import {
  EMPTY,
  filter,
  first,
  fromEvent,
  mergeMap,
  Observable,
  share,
  takeUntil,
} from 'rxjs';
import { CLOSE_EVENT } from '@nestjs/websockets/constants';

enum READY_STATE {
  CONNECTING_STATE = 0,
  OPEN_STATE = 1,
  CLOSING_STATE = 2,
  CLOSED_STATE = 3,
}

export class WsAdapter extends OriginWsAdapter {
  public bindMessageHandlers(
    client: any,
    handlers: MessageMappingProperties[],
    transform: (data: any) => Observable<any>,
  ) {
    const close$ = fromEvent(client, CLOSE_EVENT).pipe(share(), first());
    const source$ = fromEvent(client, 'message').pipe(
      mergeMap((data: any) =>
        this.bindMessageHandler(data.data, handlers, transform).pipe(
          filter((result: any) => result),
        ),
      ),
      takeUntil(close$),
    );
    const onMessage = (response: any) => {
      if (client.readyState !== READY_STATE.OPEN_STATE) {
        return;
      }
      client.send(response);
    };
    source$.subscribe(onMessage);
  }

  bindMessageHandler(
    buffer: string | number | Buffer,
    handlers: MessageMappingProperties[],
    transform: (data: any) => Observable<any>,
  ) {
    try {
      const messageHandler = handlers.find(
      	// 这里的'message'值对应@SubscribeMessage传的值
        (handler) => handler.message === 'message',
      );
      const { callback } = messageHandler;
      return transform(callback(buffer));
    } catch {
      return EMPTY;
    }
  }
}

版本信息

node: 16.13.0
nestjs: 8.4.7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值