使用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:\n', 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 按钮。

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

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

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

发送hello2测试数据

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

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

发送一个错误格式的数据

{
"event" : "hello2

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

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

小结

要使用WebSocket功能,需要增加

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

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

参考

NestJS WebSocket 开始使用的更多相关文章

  1. 漫扯:从polling到Websocket

    Http被设计成了一个单向的通信的协议,即客户端发起一个request,然后服务器回应一个response.这让服务器很为恼火:我特么才是老大,我居然不能给小弟发消息... 轮询 老大发火了,小弟们自 ...

  2. 细说WebSocket - Node篇

    在上一篇提高到了 web 通信的各种方式,包括 轮询.长连接 以及各种 HTML5 中提到的手段.本文将详细描述 WebSocket协议 在 web通讯 中的实现. 一.WebSocket 协议 1. ...

  3. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  4. WebSocket - ( 一.概述 )

    说到 WebSocket,不得不提 HTML5,作为近年来Web技术领域最大的改进与变化,包含CSS3.离线与存储.多媒体.连接性( Connectivity )等一系列领域,而即将介绍的 WebSo ...

  5. php+websocket搭建简易聊天室实践

    1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...

  6. Demo源码放送:打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!

    随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...

  7. Cowboy 开源 WebSocket 网络库

    Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Pro ...

  8. 借助Nodejs探究WebSocket

    文章导读: 一.概述-what's WebSocket? 二.运行在浏览器中的WebSocket客户端+使用ws模块搭建的简单服务器 三.Node中的WebSocket 四.socket.io 五.扩 ...

  9. 细说websocket - php篇

    下面我画了一个图演示 client 和 server 之间建立 websocket 连接时握手部分,这个部分在 node 中可以十分轻松的完成,因为 node 提供的 net 模块已经对 socket ...

随机推荐

  1. Java设计模式(3:接口隔离原则和迪米特法则详解)

    一.接口隔离原则 使用多个接口,而不使用单一的接口,客户端不应该依赖它不需要的接口.尽量的细化接口的职责,降低类的耦合度. 我们先来看一个例子: 小明家附近新开了一家动物园,里面有老虎.鸟儿.长颈鹿. ...

  2. springboot的restful风格获取请求中携带的参数

    http://localhost:8080/emp/1 有以上请求,我们controller要怎么获取请求中传递的参数1呢? 通过PathVariable注解,如下: @DeleteMapping(& ...

  3. Linkerd 2.10(Step by Step)—4. 如何配置外部 Prometheus 实例

    Linkerd 2.10 系列 快速上手 Linkerd v2 Service Mesh(服务网格) 腾讯云 K8S 集群实战 Service Mesh-Linkerd2 & Traefik2 ...

  4. 面试官:为什么Mysql中Innodb的索引结构采取B+树?

    前言 如果面试官问的是,为什么Mysql中Innodb的索引结构采取B+树?这个问题时,给自己留一条后路,不要把B树喷的一文不值.因为网上有些答案是说,B树不适合做文件存储系统的索引结构.如果按照那种 ...

  5. 【题解】覆盖问题 BZOJ1052 HAOI2007 二分

    题目描述 某 人在山上种了N棵小树苗.冬天来了,温度急速下降,小树苗脆弱得不堪一击,于是树主人想用一些塑料薄膜把这些小树遮盖起来,经过一番长久的思考,他决定用 3个LL的正方形塑料薄膜将小树遮起来.我 ...

  6. 树的计数(prufer序列 或 purfer序列)

    题解 首先我们要知道一条性质,prufer序列中的某个点出现次数为该点在树中度数-1 感性理解一下,其实按照prufer序列求法自己推一下就出来了 设题目里给的度为$d[]$ 先将所有的d-- 然后按 ...

  7. Oracle几大后台进程

    pmon,smon,dbwr,lgwr,reco,ckpt.六大核心进程.无论哪个进程出现故障,都会导致数据库实例崩溃.如果杀掉某个进程,在ALERT LOG中会发现各种错误. MMAN(Memory ...

  8. DOS命令行(3)——Windows运行窗口打开特定功能

    cmd 开启命令解释权或启用Windows中的某程序 cmd /c 执行完命令后关闭命令窗口 cmd /k 执行完命令后不关闭命令窗口   winver 查看Windows版本信息版权信息授权信息   ...

  9. python-geopandas读取、创建shapefile文件

    作者:fungis 描述:一个热带生活.乐于分享.努力搬砖的giser 交流邮箱:fungis@163.com shapefile是GIS中非常重要的一种数据类型,在ArcGIS中被称为要素类(Fea ...

  10. 微信sdk上传图片大小1k,损坏的问题以及微信上传图片需要的配置

    微信公众号的appid和appsecret有问题,会导致上传图片大小为1k这个问题 微信上传图片需要设置公众号的'JS接口安全域名'