likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 12

后端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、安装

npm install socket.io

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

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

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

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、安装

npm install socket.io-client

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

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(地方可自行选择)

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

结语

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