第一章 Socket.IO的前后端引入和使用
前言
此文章来源于我的CSDN。
从2022年底开始抽空当了个全栈开发,前端、后端、服务器全部一个人搞定,做了一些简单的第三方登录、增删改查等功能,正在一筹莫展不知道还能加什么功能时,我想到了我经常逛的一个社交聊天平台 discord,那我能不能模仿它的界面做一个聊天室呢?
所用到的技术栈
前端 | 后端 |
---|---|
Angular版本14 + Ant Design of Angular | NodeJs + express + Ts |
socket.io-client | socket.io |
Github地址 | Github地址 |
预览在线地址:www.evziyi.top,其余功能还在持续开发。
一、socket.io是什么?
根据官方的回答:Socket.IO 是一个库,可以在客户端和服务器之间实现 低延迟, 双向 和 基于事件的 通信。它建立在 WebSocket
协议之上,并提供额外的保证,例如回退到 HTTP 长轮询或自动重新连接。
有了是什么,官方还给出了它不是什么:Socket.IO 不是 WebSocket实现
他的连接方案分为五步:
- 握手 (包含会话 ID — 此处, zBjrh…AAAK — 用于后续请求)
- 发送数据 (HTTP 长轮询)
- 接收数据 (HTTP 长轮询)
- 升级 (WebSocket)
- 接收数据 (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
提示:客户端和服务端的版本不能差异过大,具体可以看文档的说明
2. 使用
先了解主要初始化方法:
首先创建一个连接,使用服务器的 URLsocketIo = 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
系统消息的事件名
我们聊天室功能的基本设计流程图如下:
前端使用
前端创建一个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};
当前端发送消息时看到的效果如下:
绿色箭头为发送,红色箭头为接收,消息发送成功会有回调的红色消息
因为我们在后端设置了pingInterval: 30000
,所以它会定时推送心跳消息给前端,防止中断连接
总结
这一篇主要介绍了制作聊天室的由来和Socket.IO的使用方法,回顾一下核心要素就是以下几步:
- 初始化
socketIo = io('ws://服务端url/');
- 连接成功
socketIo.on('connect', () => {});
- 发送:
socket.emit('事件名', (evt) => {});
- 接收:
socket.on('事件名', (evt) => {});
以上代码都可以在我的Github上面找到:前端Github地址 | 后端Github地址 ,写的不错的的可以点个标星哦,其余功能还在持续开发中,在线地址:www.evziyi.top。