webSocket 实现消息推送、心跳、已读消息、加载更多等功能

读万卷书不如行万里路,行万里路不如阅人无数,阅人无数不如名师指路。站着巨人身上,你可以看的更高、更远。

一、webSocket 初始化

initWebSocket 方法中,初始化 webSocket。

this.socket = new Socket('ws://192.168.11.198:12000/msg', {
  userId: '1234',
  token: 'token-demo',
  sysCode: '001',
})

在文件中,引入 socket.js 文件(webSocket 的基本配置)。

export default class {
  constructor (url, opts = {}) {
    ...
    this.init(url, opts)
  }
}

调用 init 初始化方法,连接 websocket,同时绑定系统事件。

init (url, opts) {
  this.client = new WebSocket(url)
  .....
  return this.client
}

webSocket 相关事件

this.client.onopen = () => {}  // 连接建立时触发
this.client.onmessage = (event) => {} // 客户端接收服务端数据时触发
this.client.onerror = () => {}  // 通信发生错误时触发
this.client.onclose = () => {}  // 连接关闭时触发

二、客户端与服务端建立连接

在建立连接时,调用 login 方法。

this.client.onopen = () => {
  this.login(opts.userId, opts.sysCode, opts.token)
}

在登陆方法中,调用 send 方法。

login (userId, sysCode, token) {
  const operation = 'Auth'
  ......
  this.send(operation, data, token)
}

在 send 方法中, 首先检查 websocket 的连接状态。

this.client.readyState 为 1 时,表示 webSocket 已经连接成功,可以通讯。调用 this.client.send 方法发送信息。

/**
 * 发送事件
 * @param {string} operation 事件类型
 * @param {object} data 事件参数
*/
send (operation, data) {
  const readyState = this.client.readyState

  return new Promise((resolve, reject) => {
    if (this.client.readyState === 1) {
      const message = {
        operation,
        data,
        token: this.token,
      }
      this.client.send(JSON.stringify(message))
      resolve()
    } else {
      reject(new Error(`当前连接状态为:${readyState}, 无法发送消息`))
    }
  })
}

接收来自服务端的消息。

this.client.onmessage = (event) => {
  const resp = JSON.parse(event.data)
  ......
  this.events[resp.operation](resp) // event 上绑定触发类型和结果。
}

在业务逻辑中,对绑定的登录事件进行回调。在登录成功后,回调服务器推送过来的消息,同时将 result 中返回的 token,传入调用心跳方法中。

this.socket.on('Auth', (result) => {
  console.log('Auth 回调', result)
  ......
  // 启动心跳
  this.socket.heartStart(this.token)
})

on 绑定自定义事件。

on (operation, func) {
  this.events[operation] = func
}

三、启动心跳(定时器)

采用定时器,在一定的时间间隔内,给服务端发送一次消息,说明客户端连接到服务端的。

heartStart (token) {
  this.heartTimer = window.setInterval(() => {
    this.heartBeat(token)
  }, this.heartInterval)
  this.heartBeat(token)
  ......
}

在心跳方法中,调用 send 方法。

heartBeat (token) {
  const operation = 'HeartBeat'
  ...
  this.send(operation, data, token)
}

同时在 heartStart 方法中,绑定回调事件。

heartStart (token) {
  ......
  this.on('HeartBeat', () => {
    console.log('接送到心跳回应')
  })
}

接收来自服务端的消息。

this.client.onmessage = (event) => {
  const resp = JSON.parse(event.data)
  ......
  this.events[resp.operation](resp) // event 上绑定触发类型和结果。
}

最后绑定的 HeartBeat 事件回调中,打印信息:接送到心跳回应。

四、下拉获取分页数据

如何在滚动列表时,添加触底事件,向服务端发送获取该类型列表的下一页数据呢。

在触底事件中,只需添加下面代码:

this.socket.msgPull(this.token, type, row.id)

在消息分页 msgPull 方法中,调用 send 方法。

msgPull (token, type, pageId, size) {
  const operation = 'Pull'
  ......
  this.send(operation, data, token)
}

send 发送事件中,使用 this.client.send(JSON.stringify(message)) 发送信息给服务器。

在 this.client.onmessage 中接收服务端返回到客户端的信息。

this.client.onmessage = (event) {
  const resp = JSON.parse(event.data)
  ......
  this.events[resp.operation](resp)
}

在 Pull 回调中,获取从服务端回调过来的信息。

this.socket.on('Pull', (result) => {
  console.log('Pull 回调', result)
})

敲敲黑板,重点来了。

1、如何监听列表滚动到底部时,调用 Pull 类型的 send 方法获取下一页数据;

2、如何区分当前滚动列表需要加载下一页的数据类型;

3、若该类型列表无更多数据,触底事件频繁调用获取下一页数据,如何优化呢?

调用 handleInfiniteOnLoad 方法时,会传入列表的类型,我们会将需要加载下一页数据的类型传给服务端的;在 Pull 操作类型的回调中,我们将返回的结果进行判断。

this.socket.on('Pull', (result) => {
  this.loading = false
  // end: '1' => 结束,没有下一页, '0' => 有下一页
  if (result.data.end === '0') {
    switch (result.data.type) {
      case 'INFORM':
        this.msgTable.informs = [...this.msgTable.informs, ...result.data.messages]
        break
      ......
    }
  } else {
    // 没有下一页时移除滚动事件
    if (!this.noMoreFlagList.includes(result.data.type)) {
      this.noMoreFlagList.push(result.data.type)
    }
  }
})

noMoreFlagList 为没有下一页的列表类型;该类型列表无更多数据,禁止提交 pull 操作类型的事件。

handleInfiniteOnLoad (type) {
  let row
  if (type === 'INFORM') {
    row = this.msgTable.informs[this.msgTable.informs.length - 1]
  } 
  ......
  // 匹配标识是否已没有下一页
  if (!this.noMoreFlagList.includes(type)) {
    this.loading = true
    this.socket.msgPull(this.token, type, row.id)
  }
},

 

五、后端主动推送消息

当后端主动推送消息时,我们又应该如何接收呢?

在服务端推送消息给客户端时,会获取消息的类型和数据,对应的在消息回调中可以获取相应的数据。

initWebSocket () {
  ......
  // 后端主动推送消息
  this.socket.on('Push', (result) => {
    console.log('push', result)
    this.$message.info({
      message: '您好',
      description: '您有新的未读消息,请注意查收',
    })
    switch (result.data.type) {
      case 'INFORM':
        this.msgTable.informs.unshift(result.data)
        break
      ......
    }
    // 未读总数+1
    this.totalUnreadCount++
  })
},

六、已读消息处理

首先用户点击浏览器,调用 handleRead 事件。

handleRead (msgId, status) {
  // 已读消息则不再发起阅读
  if (status === '0' && !this.readedList.includes(msgId)) {
    this.socket.msgRead(this.token, msgId)
  }
},

在 msgRead 方法中,调用【已读】类型的 send 方法。

msgRead (token, msgId) {
  const operation = 'Read'
  ......
  this.send(operation, data, token)
}

在业务逻辑中,对已读进行回调,将返回的结果 id 放入 readedList 已读列表中,同时对未读数量 totalUnreadCount 进行 -1 操作。

this.socket.on('Read', (result) => {
  this.readedList.push(result.data.msgId)
  // 未读总数-1
  this.totalUnreadCount--
})

页面展示已读与未读

// item.vue 组件
<div
  class="z-msg__item"
  :class="{'z-msg__item--read': (itemData.status === '1' || judgeReaded(itemData.id))}"
  @click="itemClick(itemData)"
>
  ......
</div>

status 为 1 或者 readedList 数组中存在点击过的消息 Id时,则为已读状态;

// 处理已读
judgeReaded (msgId) {
  if (this.readedList.includes(msgId)) {
    return true
  } else {
    return false
  }
},
z-msg__item--read {
  opacity: 0.6;
}

注意:首先通过 userId 和 token 与用户相关的字段,与服务端建立连接(用于身份标识,这里的 userId 和 token 是登录系统后的用户信息)。在建立连接之后的回调中,返回的 result 里面有个 token 字段。这个由服务端返回的字段,在后续的 HeartBeat、Pull、Push、Read 类型的 send 方法中,作为客户端与服务端的传递消息的桥梁。

Spring Boot是一个开源的Java开发框架,它提供了许多对开发者友好的功能和工具。其中,Spring Boot还支持WebSocket,使得开发者能够轻松地实现消息推送功能。 在Spring Boot中使用WebSocket实现消息推送,需要以下几个步骤: 1. 添加WebSocket依赖:在项目的pom.xml文件中添加Spring Boot WebSocket依赖。 2. 创建WebSocket配置类:新建一个WebSocketConfig.java类,并在该类中添加@Configuration和@EnableWebSocket注解,以启用WebSocket。 3. 实现WebSocketHandle类:新建一个WebSocketHandler类,并继承TextWebSocketHandler类或实现WebSocketHandler接口,以处理WebSocket相关的请求和消息。在该类中,可以覆盖父类或接口的方法,如handleTextMessage()方法用于处理文本消息。 4. 添加WebSocket处理器:在WebSocketConfig.java类中,重写registerWebSocketHandlers()方法,并添加WebSocket处理器(即上一步创建的WebSocketHandler类)及对应的处理路径。 5. 前端页面:在前端页面中,使用WebSocket API与后端进行通信,并接收和处理后端发送的消息。 通过以上步骤,就完成了Spring Boot WebSocket的基本配置和实现。在实际使用中,可以根据具体需求,添加一些其他功能,如身份认证、消息广播、点对点消息等。 总结起来,Spring Boot通过提供WebSocket的支持,使得开发者能够方便地实现消息推送功能。通过简单的配置和编写处理器类,就可以实现与前端的实时通信。这样,开发者可以轻松地在项目中应用消息推送功能实现好的用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值