后端koa2前端ReactHooks中完成socket长连接(附源码)

后端koa2前端ReactHooks中完成socket长连接(附源码)

webSocket TCP长连接在平时中常用于一些高频前后端交互的场景,比如在线聊天、在线协作等;在以前时,一些项目中的通知与公告功能常用轮询来完成类webSocket的操作,但是这种方法频繁三次握手访问后端服务器,对服务器造成不小的压力,所以用webSocke长连接取代轮询是很有必要的;本次我就个人的开源项目里面的通知与公告功能来做案例,文章手把手帮您完成项目的socket的配置;项目源码在我个人的开源项目中,如果觉得可以,欢迎给作者收藏点赞~ 🥗

个人开源项目地址

一、了解socket.IO

一开始我个人是想选择koa-websocket的,但是实际使用后,发现这个文档不全、生态不够丰富,并且和我的项目并不是很契合,最终放弃了它,选择了socket.IOsocket.IO不仅官网文档很全,案例丰富,而且还特别契合我现在的项目,所以最终选择了它,实际使用感受确实好用;socket.IO是一个建立在webSocket协议上的库,它在服务器和客户端之间提供双向和低延迟的通信通道;

Socket.IO官方文档

二、koa2服务端使用socket.IO

2-1、安装

lua 复制代码
npm install socket.io

2-2、创建实例,并绑定到HTTP服务器

appuse注册页面,导入socket.iocreateServer创建实例绑定到HTTP服务器上,共用一个ip端口;cros解决跨域问题,io.of()配置独立的命名空间,.on监听连接事件,wsNotice是我个人封装的处理事件的函数;

ts 复制代码
import { Server } from 'socket.io' 

// ...中间一系列app.use注册,详细代码可下载源码查看

// 创建 Socket.IO 实例并绑定到 HTTP 服务器
const httpServer = createServer(app.callback())
const io = new Server(httpServer, {
  cors: {
    origin: '*',
    methods: ['GET', 'POST']
  }
})

// 在koa2应用中定义各种Socket.IO事件监听和处理
io.of('/wsNotice').on('connection', (socke封装t) => wsNotice(socket))

2-3、封装处理事件函数wsNotice

ts 复制代码
import SysNotice from '@/mysql/model/system/notice.model'
import { Socket } from 'socket.io'
import { DefaultEventsMap } from 'socket.io/dist/typed-events'
import { formatHumpLineTransfer } from '.'
import { queryConditionsData } from '../service'
import jwt from 'jsonwebtoken'
import env from '@/config/default'
import { IuserTokenType } from '@/types/auth'
import { judgeKeyOverdue } from './auth'

export const wsNotice = async (
  socket: Socket<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, any>
) => {
  const token = socket.handshake.query.token

  if (judgeToken(token as string)) {
    socket.on('postNotice', async (data) => {
      // 向所有的socket发送通知
      socket.broadcast.emit('getNotice', data)
      socket.emit('getNotice', data)

      // 向所有的socket更新通知公告
      socket.emit('getAllNotice', await getAllNoticeFn())
      socket.broadcast.emit('getAllNotice', await getAllNoticeFn())
    })

    socket.emit('getAllNotice', await getAllNoticeFn())

    socket.on('disconnect', (reason) => {
      console.log('socket已断开')
    })
  } else {
    socket.disconnect(true)
    console.error('token过期,断开socket连接')
  }
}

/**
 * 查询所有通知公告
 * @returns
 */
async function getAllNoticeFn() {
  return formatHumpLineTransfer(await queryConditionsData(SysNotice, { status: '0' }))
}

/**
 * 判断token是否有效
 * @param token
 * @returns
 */
async function judgeToken(token: string) {
  const user = jwt.verify(token, env().JWT_SECRET) as IuserTokenType
  return await judgeKeyOverdue(user.session)
}

其中向所有的socket连接发送消息,也可以选择使用socket.IO中的广播事件和房间,因为这里的需求很简单,所以就用最基本的操作了,可以按照自己的实际需求更改的哈~

三、ReactHooks客户端端使用socket.IO

客户端安装 socket.io-client 这个包,然后配置通信地址即可;

3-1、安装

lua 复制代码
npm install socket.io-client

3-2、封装创建socket的函数

tsx 复制代码
import useStore from '@/store'
import { InoticeType } from '@/type/modules/system/notice'
import { notification } from 'antd'
import { io } from 'socket.io-client'

const createSocket = (pathName: string) => {
  const {
    useSocketStore: { setNotices },
    useUserStore: { token },
  } = useStore()
  
  // forceNew开启命名空间,query携带用户token
  const socket = io(`${process.env.BASE_ENV}/${pathName}`, { forceNew: true, query: { token }})

  socket.on('connect', () => {
    console.log('socket是否连接成功' + socket.connected) // true
  })
  socket.on('connect_error', (err) => {
    console.log('socket连接失败', err)
    setTimeout(() => {
      socket.connect()
    }, 5000)
  })

  // 获取所有公告和通知
  socket.on('getAllNotice', (data: InoticeType[]) => {
    setNotices(data)
  })
  // 获取单条公告和通知
  socket.on('getNotice', (data: InoticeType) => {
    notification.info({
      message: data.noticeTitle,
      description: <div dangerouslySetInnerHTML={{ __html: data.noticeContent as string }} />,
    })
  })

  socket.on('disconnect', (reason) => {
    // 服务器断开socket连接时
    if (reason === 'io server disconnect') {
      socket.connect()
    }
    console.log('socket已断开', reason)
  })

  return socket
}

export default createSocket

3-3、在layout初始化的时候连接socket(地方可自行选择)

scss 复制代码
  useEffect(() => {
    // 初始化连接socket
    const socket = createSocket('wsNotice')
    // 存储socket实例,在通知模块,立即通知处需要用到socket.emit事件立即向所有用户发送通知信息
    setSocket(socket)
  }, [])

结语

以上用socket.IO完成最基础的通知与公告功能,如果自己有更复杂的业务,可以自行官网研究~🌮

相关推荐
Java程序员威哥1 分钟前
Spring Boot 3.x 云原生终极适配:GraalVM 原生镜像构建 + Serverless 生产级部署(完整实战+最优模板)
java·开发语言·spring boot·后端·云原生·serverless·maven
qq_12498707533 分钟前
基于微信小程序宠物服务系统(源码+论文+部署+安装)
java·spring boot·后端·微信小程序·小程序·毕业设计·宠物
MC丶科7 小时前
【SpringBoot常见报错与解决方案】中文乱码?Spring Boot 统一解决前后端中文乱码问题(含 Postman 测试)!别再百度“加 UTF-8”了!
spring boot·后端·postman
XXOOXRT12 小时前
基于SpringBoot的加法计算器
java·spring boot·后端·html5
moxiaoran575312 小时前
Go语言的错误处理
开发语言·后端·golang
南村群童欺我老无力.17 小时前
Flutter 框架跨平台鸿蒙开发 - 开发双人对战五子棋游戏
flutter·游戏·华为·typescript·harmonyos
短剑重铸之日18 小时前
《7天学会Redis》特别篇: Redis分布式锁
java·redis·分布式·后端·缓存·redission·看门狗机制
小北方城市网18 小时前
SpringBoot 全局异常处理与接口规范实战:打造健壮可维护接口
java·spring boot·redis·后端·python·spring·缓存
hanqunfeng19 小时前
(三十三)Redisson 实战
java·spring boot·后端
小北方城市网20 小时前
SpringBoot 集成 MyBatis-Plus 实战(高效 CRUD 与复杂查询):简化数据库操作
java·数据库·人工智能·spring boot·后端·安全·mybatis