从零开始使用Socket.IO加Node搭建聊天室【第一篇】

第一章 Socket.IO的前后端引入和使用

前言

此文章来源于我的CSDN

从2022年底开始抽空当了个全栈开发,前端、后端、服务器全部一个人搞定,做了一些简单的第三方登录、增删改查等功能,正在一筹莫展不知道还能加什么功能时,我想到了我经常逛的一个社交聊天平台 discord,那我能不能模仿它的界面做一个聊天室呢?

从零开始使用Socket.IO加Node搭建聊天室【第一篇】


所用到的技术栈

前端 后端
Angular版本14 + Ant Design of Angular NodeJs + express + Ts
socket.io-client socket.io
Github地址 Github地址

预览在线地址:www.evziyi.top,其余功能还在持续开发。

一、socket.io是什么?

Socket.Io官方文档

根据官方的回答:Socket.IO 是一个库,可以在客户端和服务器之间实现 低延迟, 双向 和 基于事件的 通信。它建立在 WebSocket 协议之上,并提供额外的保证,例如回退到 HTTP 长轮询或自动重新连接。
从零开始使用Socket.IO加Node搭建聊天室【第一篇】

有了是什么,官方还给出了它不是什么:Socket.IO 不是 WebSocket实现

他的连接方案分为五步:
从零开始使用Socket.IO加Node搭建聊天室【第一篇】

  1. 握手 (包含会话 ID — 此处, zBjrh…AAAK — 用于后续请求)
  2. 发送数据 (HTTP 长轮询)
  3. 接收数据 (HTTP 长轮询)
  4. 升级 (WebSocket)
  5. 接收数据 (HTTP 长轮询, WebSocket连接建立成功后关闭)

二、安装

1. 引入库

客户端引入

 //npm
 npm install socket.io
 //yarn
 yarn add socket.io
 //pnpm
 pnpm add socket.io

服务端引入

 //npm
 npm install socket.io-client
 //yarn
 yarn add socket.io-client
 //pnpm
 pnpm add socket.io-client

提示:客户端和服务端的版本不能差异过大,具体可以看文档的说明
从零开始使用Socket.IO加Node搭建聊天室【第一篇】

2. 使用

先了解主要初始化方法:
首先创建一个连接,使用服务器的 URL socketIo = io('ws://127.0.0.1:3011/', opt);
连接成功后使用监听事件socketIo.on('connect', () => {});,其中connect为连接成功参数。
其余的参数还有断开连接:disconnect连接错误:connect_error

现在知道了初始化的方法,下面讲解一下如何传递参数的

emit为发送方法,例如:

socket.emit('hello','jake');

其中hello参数为发送事件的名称,jake为发送的数据,相对应的接收方(服务器)想要接收到发送方的数据则需要设置同样的事件名称才能接收到:

socket.on('hello',(message: string) => {
	console.log(message);
	// jake
});

同样的,服务端(后端)发送数据给客户端(前端)也是这样的方法,发送方使用emit发送,接收方使用on接收。

为了我们聊天系统的优雅,我们可以设置两种消息类型的事件名称:
publicMessage 公共聊天的消息事件名
systemMessage 系统消息的事件名

我们聊天室功能的基本设计流程图如下:
从零开始使用Socket.IO加Node搭建聊天室【第一篇】

前端使用

前端创建一个SocketIoService服务组件,核心代码如下:

  // 全局变量
  socketIo: Socket;
  // messages为观察者函数,传递socket消息
  constructor(private messages: MessageService) {
  }
  /**
   * 连接
   */
  public connect(): void {
    // opt为给服务端的数据,用来获取当前用户的信息
    const opt = {
      extraHeaders: {
        role: SessionUtil.getRoleId(),
        token: SessionUtil.getToken().tokenValue
      }
    };
    // 本地测试地址
    this.socketIo = io(`ws://127.0.0.1:3011/`, opt);
    // 连接成功
    this.socketIo.on('connect', () => {
      // todo 需要让服务器保存新进来的用户资料
      console.log('websocket 连接成功', this.socketIo.id);
      // 公共频道消息
      this.socketIo.on(ChatChannelsMessageTypeEnum.publicMessage, (msg: ChatMessagesInterface) => {
        // console.log('公共频道消息', msg);
        // 转换一下格式,添加消息类型用来判断
        const message: ChatChannelSubscribeInterface = {
          type: ChatChannelsMessageTypeEnum.publicMessage,
          msg
        };
        // 通过观察者方法发射数据
        this.messages.sendMessage(message);
      });
      // 系统消息
      this.socketIo.on(ChatChannelsMessageTypeEnum.systemMessage, (msg) => {
        // console.log('系统消息', msg);
        // 转换一下格式,添加消息类型用来判断
        const message: ChatChannelSubscribeInterface = {
          type: ChatChannelsMessageTypeEnum.systemMessage,
          msg
        };
        // 通过观察者方法发射数据
        this.messages.sendMessage(message);
      });
      // 连接断开
      this.socketIo.on('disconnect', (reason) => {
        // console.log('Socket disconnected: ' + _id);
        console.log('websocket 连接断开');
      }};
      // 连接关闭
      this.socketIo.on('close', (msg) => {
        // todo 需要处理断开的用户
        console.log('websocket 连接关闭');
      });
      // 连接错误
      this.socketIo.on('connect_error', (msg) => {
        // todo 需要处理断开的用户
        console.log('websocket 连接错误');
      });
    });
  }

需要注意的是所有的方法都需要在连接成功的事件里面执行:socketIo.on('connect', () => {});

后端使用

后端新建socket.ts的Ts文件,核心代码如下:

/**
 * websocket服务
 * 使用socket.io
 */
const app = express();
const SocketServer = http.createServer(app);
const io = new Server(SocketServer, {
    cors: {
        origin: '*',
    },
    connectionStateRecovery: {
        // 会话和报文的备份时间
        maxDisconnectionDuration: 1 * 60 * 1000,
        // 恢复成功后是否跳过中间件
        skipMiddlewares: true,
    },
    // 发送新的ping packet(30000)之前有多少ms
    pingInterval: 30000,
    // 有多少ms没有传递消息则考虑连接close(5000)
    pingTimeout: 5000,
});

/**
 * 连接
 */
io.on('connection', async (socket) => {
    console.log('socket连接成功!', socket.id);

    /**
     * 接收公共频道消息
     * publicMessage 为规定好的事件名称
     */
    socket.on(ChatChannelsMessageTypeEnum.publicMessage, (parseMessage: ChatMessagesInterface, callback) => {
        try {
            // console.log('公共频道消息', parseMessage);
            // 接收消息成功回调
            callback({
                status: ChatChannelsCallbackEnum.ok
            });
        } catch (e) {
            // 接收消息失败回调
            callback({
                status: ChatChannelsCallbackEnum.error
            });
        }
    });

    /**
     * 接收系统消息
     * systemMessage 为规定好的事件名称
     */
    socket.on(ChatChannelsMessageTypeEnum.systemMessage, (message: any, callback) => {
        console.log('接收系统消息', message);
        try {
            // 接收消息成功回调
            callback({
                status: ChatChannelsCallbackEnum.ok
            });
        } catch (e) {
            callback({
                status: ChatChannelsCallbackEnum.error
            });
        }
    });
    /**
     * 连接断开
     */
    socket.on('disconnect', () => {
        console.log('连接断开', socket.id);
    });
    /**
     * 连接关闭
     */
    socket.on('close', () => {
        console.log('连接关闭', socket.id);
    });
    /**
     * 连接错误
     */
    socket.on('connect_error', () => {
        console.log('连接错误', socket.id);
    });
});

export {SocketServer, io};

当前端发送消息时看到的效果如下:
从零开始使用Socket.IO加Node搭建聊天室【第一篇】

绿色箭头为发送,红色箭头为接收,消息发送成功会有回调的红色消息
因为我们在后端设置了pingInterval: 30000,所以它会定时推送心跳消息给前端,防止中断连接


总结

这一篇主要介绍了制作聊天室的由来和Socket.IO的使用方法,回顾一下核心要素就是以下几步:

  1. 初始化socketIo = io('ws://服务端url/');
  2. 连接成功socketIo.on('connect', () => {});
  3. 发送:socket.emit('事件名', (evt) => {});
  4. 接收:socket.on('事件名', (evt) => {});

以上代码都可以在我的Github上面找到:前端Github地址 | 后端Github地址 ,写的不错的的可以点个标星哦,其余功能还在持续开发中,在线地址:www.evziyi.top

原文链接:https://juejin.cn/post/7244604894408966200 作者:ZzzYi

(0)
上一篇 2023年6月17日 上午10:58
下一篇 2023年6月17日 上午11:08

相关推荐

发表回复

登录后才能评论