NestJS WebSocket 开始使用

摘要:
使用NestJs提供WebSocket服务。nestnewwebsocket-start得到一个有基础功能的工程。进入项目目录,安装2个库npmi--save@nestjs/websockets@nestjs/platform-socket.io启动使用端口3001awaitapp.listen;npmrunstart启动我们的工程。gateway介绍Nest里的gateway(网关)只是一个用@WebSocketGateway()装饰器注释的类。从技术上讲,网关与平台无关,在创建适配器后它们与任何WebSockets库都兼容。获取WebSocket对象在WsStartGateway里新增加一个消息订阅方法。方法里接受@ConnectedSocket()client:WebSocket,这个client就是与客户端的连接对象。@SubscribeMessagehello2:any{console.log;client.send;return{event:'hello2',data:data};}自定义WebSocketAdapter前面我们建立好了Gateway,还需要一个适配器。新建文件ws.adapter.ts,继承WebSocketAdapterimport*asWebSocketfrom'ws';import{WebSocketAdapter,INestApplicationContext}from'@nestjs/common';import{MessageMappingProperties}from'@nestjs/websockets';import{Observable,fromEvent,EMPTY}from'rxjs';import{mergeMap,filter}from'rxjs/operators';exportclassWsAdapterimplementsWebSocketAdapter{constructor{}create:any{console.logreturnnewWebSocket.Server;}bindClientConnect{console.log;server.on;}bindMessageHandlers{console.logfromEvent.pipe.subscribe;}bindMessageHandler:Observable{letmessage=null;try{message=JSON.parse;}catch{console.log;returnEMPTY;}constmessageHandler=handlers.find;if(!判断依据是message.event,就是event字段。

使用NestJs提供WebSocket服务。
本文会在新建项目的基础上增加2个类

  • Gateway 实现业务逻辑的地方
  • WebSocketAdapter WebSocket适配器

新建项目

新建一个项目来演示,用npm来管理项目。

nest new websocket-start

得到一个有基础功能的工程。

进入项目目录,安装2个库

npm i --save @nestjs/websockets @nestjs/platform-socket.io

启动

使用端口3001

await app.listen(3001);

npm run start启动我们的工程。用postman测一下,功能ok。

gateway介绍

Nest里的gateway(网关)只是一个用 @WebSocketGateway() 装饰器注释的类。从技术上讲,网关与平台无关,在创建适配器后它们与任何 WebSockets 库都兼容。

新建Gateway

新建ws.gateway.ts文件。在装饰器@WebSocketGateway()里端口指定为3002。

import { ConnectedSocket, MessageBody, SubscribeMessage, WebSocketGateway } from "@nestjs/websockets";
import * as WebSocket from 'ws';

@WebSocketGateway(3002)
export class WsStartGateway {

  @SubscribeMessage('hello')
  hello(@MessageBody() data: any): any {
    return {
      "event": "hello",
      "data": data,
      "msg": 'rustfisher.com'
    };
  }
}

里面有一个hello方法,订阅的消息是'hello'

把它放进AppModuleproviders里。

providers: [WsStartGateway],

如果websockt和http用了同一个接口(本例是3001),启动时会报错

Error: listen EADDRINUSE: address already in use :::3001

因此我们这里给ws分配另一个端口号。

获取WebSocket对象

WsStartGateway里新增加一个消息订阅方法。
方法里接受@ConnectedSocket() client: WebSocket,这个client就是与客户端的连接对象。
我们可以用它来给客户端发送消息。

  @SubscribeMessage('hello2')
  hello2(@MessageBody() data: any, @ConnectedSocket() client: WebSocket): any {
    console.log('收到消息 client:', client);
    client.send(JSON.stringify({ event: 'tmp', data: '这里是个临时信息' }));
    return { event: 'hello2', data: data };
  }

自定义WebSocketAdapter

前面我们建立好了Gateway,还需要一个适配器。
新建文件ws.adapter.ts,继承WebSocketAdapter

import * as WebSocket from 'ws';
import { WebSocketAdapter, INestApplicationContext } from '@nestjs/common';
import { MessageMappingProperties } from '@nestjs/websockets';
import { Observable, fromEvent, EMPTY } from 'rxjs';
import { mergeMap, filter } from 'rxjs/operators';

export class WsAdapter implements WebSocketAdapter {

    constructor(private app: INestApplicationContext) { }

    create(port: number, options: any = {}): any {
        console.log('ws create')
        return new WebSocket.Server({ port, ...options });
    }

    bindClientConnect(server, callback: Function) {
        console.log('ws bindClientConnect, server:
', server);
        server.on('connection', callback);
    }

    bindMessageHandlers(
        client: WebSocket,
        handlers: MessageMappingProperties[],
        process: (data: any) => Observable<any>,
    ) {
        console.log('[waAdapter]有新的连接进来')
        fromEvent(client, 'message')
            .pipe(
                mergeMap(data => this.bindMessageHandler(client, data, handlers, process)),
                filter(result => result),
            )
            .subscribe(response => client.send(JSON.stringify(response)));
    }

    bindMessageHandler(
        client: WebSocket,
        buffer,
        handlers: MessageMappingProperties[],
        process: (data: any) => Observable<any>,
    ): Observable<any> {
        let message = null;
        try {
            message = JSON.parse(buffer.data);
        } catch (error) {
            console.log('ws解析json出错', error);
            return EMPTY;
        }

        const messageHandler = handlers.find(
            handler => handler.message === message.event,
        );
        if (!messageHandler) {
            return EMPTY;
        }
        return process(messageHandler.callback(message.data));
    }

    close(server) {
        console.log('ws server close');
        server.close();
    }
}

bindMessageHandler方法中,会将传来的json消息解析,然后发送到对应的处理器中。
这里就是发给gateway进行处理。
判断依据是message.event,就是event字段。

main.ts里使用这个适配器。

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useWebSocketAdapter(new WsAdapter(app)); // 使用我们的适配器
  await app.listen(3001);
}
bootstrap();

npm run start运行项目,准备进一步测试。

用Postman来测试WebSocket

Postman8.8.0提供了beta版的WebSocket测试功能。
New -> WebSocket Request beta新建一个WebSocket测试。当前版本还不支持保存ws的测试例子。
输入目标url ws://localhost:3002,点击连接 Connect 按钮。

p1.png

发送测试消息。在消息框里填入以下json数据。

{
    "event" : "hello",
    "data" : "测试数据"
}

p2.png

发送的数据经过WsAdapter分发给WsStartGateway,处理后返回数据。

发送hello2测试数据

{
    "event" : "hello2",
    "data" : "测试数据"
}

ws-postman-p3.png

可以看到服务返回了2条数据。

发送一个错误格式的数据

{
    "event" : "hello2

服务端接收到了数据,但是解析失败

ws解析json出错 SyntaxError: Unexpected end of JSON input

小结

要使用WebSocket功能,需要增加

  • Gateway 实现业务逻辑的地方
  • WebSocketAdapter WebSocket适配器

ws的端口建议是和http的端口分开。

参考

免责声明:文章转载自《NestJS WebSocket 开始使用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇为自己编写的windows应用程序制作安装包解决Intellij IDEA中Mybatis Mapper自动注入警告下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

win10 Jmeter5.1进行websocet压力测试笔记 服务端 net core2.2

1. win10安装jmeter5.1 参考文件 https://blog.csdn.net/Jenny_He/article/details/88926605 2. JMeter 扩展实现 WebSocket 支持 JMeter 是目前最为流行的开源性能测试工具,JMeter 本身提供的基于插件的机制允许第三方实现标准 JMeter 所不支持的协议,而...

GCC编译之后的代码信息

arm-none-eabi-gcc编译之后会显示一些信息,比如 下面就来说受每个字段的含义以及它们与bin文件大小的关系。text区是代码区,就是程序编译之后源码的区域,在烧录之后一直位于Flash ROM中。 data和bss都是指的全局变量以及函数内static的变量,区别是data是有初始值的而bss没有。data的初始值同样存在Rom里,当单片机启...

laravel Event执行顺序

  laravel一大特色就是event事件系统。一般首先要listen一个事件,随后fire那个事件,这时执行路径将会调用event handler,返回后继续执行。例如: Event::listen('user.login',function(KidsitUser $user){ var_dump($user); }); Route::g...

Asp.net Mvc 使用EF6 code first 方式连接MySQL总结

最近由于服务器变更为Linux系统.MsSql for Linux什么时候出来到生产环境使用还是要很长时间的.于是考虑使用Mysql数据库,ORM使用EF.于是先踩下坑顺便记录一下,有需要的tx可以参考下.当你考虑使用EF连接Mysql的时候肯定是已经在网上搜了一堆教程.网上教程基本都是使用控制台做演示.跟着一步步来姿势没错的话可能会正常运行,但项目中使用...

JS CustomEvent自定义事件传参

首先,看了鑫大佬的文章后,百度了文章内容的两个方法: 1、CustomEvent事件是有程序创建的,可以有任意自定义功能的事件 2、dispatchEvent()方法给节点分派一个合成事件 这两个方法之前也见过,不过并没有去了解,这次刚好又看到,就简单了解一下。好了,直接进入正文 一、addEventListener事件触发 例子,假设DOM对象变量名称是...

element ui 的 element-tree文字显示不全的问题

在elemtn-tree 树展示的时候外面设置了固定宽度,超出的文字会隐藏,认真查看过各个嵌套节点并没有发现超出隐藏的设置。这就比较尴尬。  解决方案一、给超出的文件加上滑块(缺点不够美观,如果连续都超出的话就看着比较费劲) element 树部分的代码! <el-tree     :data="data" show-...