Skip to content

NestJS Simple Chat - 分房(room)发送消息

更新日期 2022-5-11
  • 2022-5-11 创建文档
开发环境
  • macOS 12.3.1
  • Ubuntu 16.04
  • nest 8.1.5
  • pm2 4.4.1

本文目的

  • NestJS提供wss连接服务
  • 引入房间(room)概念
  • 用户加入房间后,收发同一个房间内的消息

主要使用的还是socketio的功能

gateway

使用之前创建的或者再新建一个Chat2Gateway

$ nest g gateway chat/chat2

要加入某个房间(room),使用join方法

socket.join(payload.room);

接收消息的时候,我们需要拿到socket

onJoin(socket: Socket, payload: any)

这里的socket用的是socket.io里的

import { Socket } from 'socket.io';

服务对某个room进行群发消息,可以用to指定房间

this.server.to(payload.room).emit('allMsg', payload);

Chat2Gateway完整代码

import { Logger } from '@nestjs/common';
import { OnGatewayInit, SubscribeMessage, WebSocketGateway, WebSocketServer } from '@nestjs/websockets';
import { Socket } from 'socket.io';

const opt2 = {
  path: '/chat2',
  allowEIO3: true,
  cors: {
    origin: /.*/,
    credentials: true
  }
};

@WebSocketGateway(opt2)
export class Chat2Gateway implements OnGatewayInit {
  private logger: Logger = new Logger('Chat1Gateway');

  @WebSocketServer()
  server: any;

  afterInit(server: any) {
    this.server = server;
  }

  @SubscribeMessage('join')
  onJoin(socket: Socket, payload: any): any {
    socket.join(payload.room);
    this.server.to(payload.room).emit('allMsg', payload);
  }

  @SubscribeMessage('message')
  handleMessage(client: any, payload: any): any {
    return {
      event: 'message',
      data: { code: 1, msg: "[chat2]服务 https://an.rustfisher.com" }
    };
  }

  @SubscribeMessage('m2s')
  onChatMsg(client: any, payload: any): any {
    this.logger.log(payload);
    var uid: String = payload.uid;
    var roomId = payload.room;
    if (uid != null && uid.length > 0) {
      this.server.to(roomId).emit('allMsg', payload);
    } else {
      this.logger.warn('[m2s] 用户输入uid无效');
    }
  }
}

客户端(网页)

html

先来调整一下html,增加一些控件。用户可以输入room的id。

<!DOCTYPE html>

<html>

<head>
    <meta charset="utf-8" />
    <title>Chat - an.rustfisher.com</title>

    <link rel="stylesheet" href="/css/md.css" />
    <script src="https://lib.baomitu.com/socket.io/4.4.1/socket.io.min.js"
        crossorigin="anonymous"></script>
</head>

<body>

    <h1>分房测试连接</h1>
    <p>输入昵称和Room后再进行连接</p>
    <div>
        <label style="width: 200px;">昵称</label>
        <input id="user-nickname"></input>
    </div>
    <div>
        <label>Room</label>
        <input id="room"></input>
    </div>
    <p>
        <button class="btn" id="connect">Connect</button>
        <button class="btn" id="leave">Leave</button>
    </p>
    <div>
        <input placeholder="输入消息" id="user-input"></input>
        <button class="btn" id="send">Send</button>
    </div>

    <ul id="msg-list">

    </ul>

    <script src="/js/chatroom.js"></script>

</body>

</html>
注意,html是用静态文件的方式来提供的。

css

增加一些样式

css
.btn {
    background-color: #225ece;
    border: none;
    border-radius: 2px;
    color: white;
    font-family: 'Roboto', sans-serif;
    font-size: 0.8em;
    margin: 0 0 1em 0;
    padding: 0.5em 0.7em 0.6em 0.7em;
}

.btn:active {
    background-color: #225ece;
}

.btn:hover {
    background-color: #4372ca;
}

.btn[disabled] {
    color: #ccc;
}

.btn[disabled]:hover {
    background-color: #d84a38;
}

video {
    background: #222;
    margin: 0 0 20px 0;
    --width: 100%;
    width: var(--width);
    height: calc(var(--width) * 0.75);
}

div#errorMsg p {
    color: #F00;
    font-size: small;
}

div#tipMsg p {
    color: rgb(54, 54, 54);
    font-size: small;
}

input {
    color: #4372ca;
}

ul {
    max-height: 200px;
    overflow: auto;
}

li {
    font-size: .3em;
}

js

连接上后,把room传给服务器。

'use strict';

var btnConn = document.querySelector('button#connect');
var btnLeave = document.querySelector('button#leave');
var btnSend = document.querySelector('button#send');
var userNicknameInput = document.querySelector('input#user-nickname');
var userInput = document.querySelector('input#user-input');
var roomIdInput = document.querySelector('input#room');
var msgList = document.querySelector('ul#msg-list');

var socket = null; // 连接
var uid = null;
var nickname = "RustFisher"; // 昵称
var roomId = "tmp-room1";

var nameList = ["人参", "卜芥", "儿茶", "八角", "丁香", "刀豆", "三七", "大蓟", "山药", "川乌", "天冬", "天麻", "元胡"];
var nameIndex = Math.floor(Math.random() * nameList.length);
nickname = nameList[nameIndex]; // 随机昵称
userNicknameInput.value = nickname;

// 获取浏览器指纹
const fpPromise = import('https://openfpcdn.io/fingerprintjs/v3')
    .then(FingerprintJS => FingerprintJS.load())

// Get the visitor identifier when you need it.
fpPromise
    .then(fp => fp.get())
    .then(result => {
        // This is the visitor identifier:
        uid = result.visitorId
        const visitorId = result.visitorId
        console.log(visitorId)
    })

function addMsgToList(type, msgBody) {
    var li = document.createElement("li");
    li.setAttribute("id", "newli");
    if (type == 0) {
        li.innerHTML = msgBody;
    } else if (type == 1) {
        li.innerHTML = msgBody.nickname + ": " + msgBody.msg;
    }
    msgList.appendChild(li);
}

// 发起连接
function conn() {
    if (uid == null) {
        console.error('无法连接,还没获取到uid');
        return;
    }
    console.log('try connect');

    const wssUrl = "wss://antalkws.rustfisher.com" // release
    socket = io(wssUrl, { path: '/chat2' });

    socket.on('connect', function () {
        console.log('连接成功');// 连接状态 连上
        addMsgToList(0, '连接成功');
    });

    socket.on('disconnect', (socket) => {
        console.log('socket已断开', socket);// 连接状态 已断开
        socket = null;
    });

    socket.on('debug', (data) => {
        console.log('[服务器消息][debug], ', data);
    });

    socket.on('allMsg', (data) => {
        console.log('[服务器消息][allMsg], ', data);
        addMsgToList(1, data);
    });

    socket.emit('join', {
        uid: uid, nickname: userNicknameInput.value, room: getLocalRoomId(),
        msg: '加入房间'
    });
    return true;
}

function disconnect() {
    if (socket) {
        socket.disconnect();
    }
}

function getLocalRoomId() {
    roomId = roomIdInput.value;
    if (roomId == null || roomId.length == 0) {
        console.warn('走默认room');
        roomId = 'tmp-room1';
    }
    return roomId;
}

function sendMsg() {
    var input = userInput.value;
    roomId = roomIdInput.value;
    if (input == null || input.length == 0) {
        console.warn('请输入文本');
        return;
    }
    if (roomId == null || roomId.length == 0) {
        console.warn('走默认room');
        roomId = 'tmp-room1';
    }
    if (socket) {
        socket.emit('m2s',
            {
                uid: uid,
                nickname: userNicknameInput.value,
                room: roomId,
                msg: input
            });
    }
}

btnConn.onclick = conn;
btnLeave.onclick = disconnect;
btnSend.onclick = sendMsg;

最后部署运行到服务器上

运行效果参考 chatroom - SimpleChat

示例工程 nest-sample - gitee

参考

建议详细读一下socket.io的文档 https://socket.io/docs/v4/emit-cheatsheet/ ,重点看Events部分

作者: rustfisher.com | rf.cs@foxmail.com
示例: AndroidTutorial Gitee, Tutorial Github
本文链接: https://www.an.rustfisher.com/nestjs/simple-chat/room-chat/
一家之言,仅当抛砖引玉。如有错漏,还请指出。如果喜欢本站的内容,还请支持作者。也可点击1次下方的链接(链接内容与本站无关),谢谢支持服务器