微信小程序结合SpringBoot实现WebSocket长链接

  • 引入
  • WebSocket
  • 微信小程序部分实现
    • js部分
    • 页面部分
  • 后端SpringBoot实现
    • WebSocketConfig.java
    • WebSocketEndPoint.java
    • SessionPool.java
  • 代码部分功能分析
    • 重连机制
    • 心跳机制
  • 写在最后

最近在做有关前后端的项目,前端主要是用Vue框架和微信小程序的原生框架

后端主要是采用Flask或SpringBoot

引入

我们知道页面的数据会在页面加载的时候触发created,mounted,onload等方法去后端获取数据,那么现在有一个需求,我们要做一个聊天软件,页面不刷新我们就拿不到我们想要的数据,怎么才能实时通讯呢?

还有一种需求,一个订单系统,客户发起订单后,商家难倒要一直刷新页面才能实时查看有没有新的订单吗

添加好友的模块

一些运动软件实时反馈步数等等,如果是基于前后端的web开发,那么我们可以使用WebSocket来实现前面所说的需求

WebSocket

它和Http一样是一个网络请求的协议,与Http不一样的是WebSocket是一个持久化通信协议,

传统的Http协议:发送请求获取响应(request-response)后来加入了keep-alive即在一次http链接中可以发送多次请求获取多次响应,

很明显http的协议的响应都是被动的。

WebSocket可以实现主动向前端传递消息

具体内容可以查看这篇博客:https://blog.csdn.net/frank_good/article/details/50856585

Http:
客户端:有消息吗
服务器:没有
客户端:有消息吗
服务器:没有
客户端:有消息吗
服务器:有,消息是xxxxxwebsocket:
客户端:有消息和我说
服务器:新消息:xxxx
服务器:新消息:xxxx
服务器:新消息:xxxx

微信小程序部分实现

小程序内置了websocket的api,我们直接调用即可

websocket可以放在全局,也可以放在单独的某个页面,具体看需求,我实现的是单独一个页面的

js部分

// pages/websocket/websocket.js
Page({/*** 页面的初始数据*/data: {inputStr: '',//输入字符串getStr:'',//后端获取的字符串socketStatus: 'closed', //记录websocket连接状态SocketTask:null,//socket链接后的对象,可以进行关闭发消息收消息等lockReconnect: false,//重连锁,防止重复连接wsCreateHandler: null,//重连时间句柄timeoutObj:null,//心跳检测时间句柄serverTimeoutObj:null,//心跳检测服务器响应时间句柄},//接收服务器端传送过来的数据,用于页面显示extraLine: [],//向extraLine添加一条数据add() {this.extraLine.push(this.getStr)//从服务器获取到的数据this.setData({text: this.extraLine.join('\n'),})setTimeout(() => {this.setData({scrollTop: 99999})}, 0)},/*** 生命周期函数--页面加载时触发*/onLoad: function (options) {let that = thisif (that.data.socketStatus === 'closed') {that.openSocket();}},openSocket() {try {// 连接后台服务器this.SocketTask = wx.connectSocket({url: "ws://xxxxxxxxxxxx",//你的后端地址})} catch (e) {// console.log(e)// 服务器重连this.ReConnect()}//连接成功后的操作//可以处理一些在线和非在线的情况,比如已经连接可以设置该用户头像高亮显示等等this.SocketTask.onOpen(() => {console.log('WebSocket 已连接')this.startHeartCheck()this.socketStatus = 'connected';})//断开后台服务器的操作this.SocketTask.onClose(() => {console.log('WebSocket 已断开')this.socketStatus = 'closed'this.ReConnect()})//报错时执行this.SocketTask.onError(error => {this.socketStatus = 'closed'this.ReConnect()})// 监听服务器推送的消息this.SocketTask.onMessage(message => {console.log(message)this.startHeartCheck()this.getStr = message.datalet jsonObj = JSON.parse(this.getStr)this.getStr = jsonObj.messagelet res = jsonObj.typeif (res === "pong")returnthis.add()})},// 关闭websocket服务closeSocket() {if (this.socketStatus === 'connected') {this.SocketTask.close({success: () => {this.socketStatus = 'closed'}})}},//发送消息函数sendMessage() {if (this.socketStatus === 'connected') {//群发,没有指定发送对象/*this.SocketTask.send({data: this.data.inputStr  })*///点对点发送:一般会给后端传送json数据,包括接收者的idlet jsonObj = {formUserId:"A",toUserId:"B",message:this.inputStr}let jsonStr = JSON.stringify(jsonObj)this.SocketTask.send({data: jsonStr,})}},//监听用户输入InputStr: function(e) {this.inputStr = e.detail.value},//向服务器发送数据btnFun:function(){this.sendMessage()},//重连方法ReConnect(){//如果锁被锁住就直接返回if (this.lockReconnect)returnconsole.log("重新连接...")this.lockReconnect = true//没有连接上会一直重连,为了防止请求次数过多一般设置等待时间this.wsCreateHandler && clearTimeout(this.wsCreateHandler)this.wsCreateHandler = setTimeout(()=>{this.openSocket()this.lockReconnect = false},2000)},// 心跳检测部分:网络中断等问题系统无法捕获,需要用心跳检测实现重连// 重启心跳检测resetHeartCheck(){clearTimeout(this.timeoutObj)clearTimeout(this.serverTimeoutObj)this.startHeartCheck()},//开启心跳检测定时器startHeartCheck(){this.timeoutObj && clearTimeout(this.timeoutObj)this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj)this.timeoutObj = setTimeout(()=>{console.log("发送ping到服务器")try {this.SocketTask.send({data: "ping"})} catch(e) {console.log("发送ping失败")}//内嵌计时器,防止刚连上后立刻重连this.serverTimeoutObj = setTimeout(()=>{//没有收到后台的数据关闭连接后重连// this.closeSocket()this.ReConnect()},15000)},15000)}
})

页面部分

<!--pages/websocket/websocket.wxml-->
<text>输入文字</text>
<input class="weui-input" auto-focus placeholder="文本输入框" bindinput="InputStr"/>
<button bindtap="btnFun">确定输入</button>
<view scroll-y="true" scroll-top="{{scrollTop}}"><text>{{text}}</text>
</view>

后端SpringBoot实现

WebSocketConfig.java

//将socket服务注入spring
package xihema.websocket.car.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

WebSocketEndPoint.java

//实现websocket的主要方法,在这里接收消息,发送消息
package xihema.websocket.car.websocket;import com.alibaba.fastjson.JSON;
import org.springframework.stereotype.Component;import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;//对外公布的一个后端站点
//ws://localhost:8080/websocket/用户id
@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocketEndPoint {//与某个客户端的连接会话,需要他来给客户端发送数据private Session session;//连接建立成功调用的方法@OnOpenpublic void onOpen(Session session, @PathParam("userId") String userId) {//把会话加入连接池中//userId通过用户传入,session是系统自动产生SessionPool.sessions.put(userId, session);//TODO 可以添加日志操作}//关闭会话的时候@OnClosepublic void onClose(Session session) throws IOException {SessionPool.close(session.getId());session.close();}//接收客户端的消息后调用的方法,在这里可以进行各种业务逻辑的操作@OnMessagepublic void onMessage(String message, Session session) {System.out.println(message);//心跳检测if (message.equalsIgnoreCase("ping")) {try {Map<String, Object> params = new HashMap<>();params.put("type", "pong");session.getBasicRemote().sendText(JSON.toJSONString(params));} catch (Exception e) {e.printStackTrace();}return;}//其他情况//将Json字符串转为键值对
//        Map<String, Object> params = JSON.parseObject(message, new HashMap<String, Object>().getClass());
//        SessionPool.sendMessage(params);//这里的业务逻辑仅仅是把收到的消息返回给前端SessionPool.sendMessage(message);}//心跳检测:用于服务器断开后进行感知,感知是否存活
}

SessionPool.java

//session池,用于多用户的会话保持
package xihema.websocket.car.websocket;import javax.websocket.Session;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class SessionPool {//key-value : userId - 会话(系统创建)public static Map<String, Session> sessions = new ConcurrentHashMap<>();//避免多线程问题public static void close(String sessionId) {//sessionId是在session中添加了一个标识,准确定位某条sessionfor (String userId : SessionPool.sessions.keySet()) {Session session = SessionPool.sessions.get(userId);if (session.getId().equals(sessionId)) {sessions.remove(userId);break;}}}public static void sendMessage(String userId, String message) {sessions.get(userId).getAsyncRemote().sendText(message);}//消息的群发,业务逻辑的群发public static void sendMessage(String message) {for (String sessionId : SessionPool.sessions.keySet()) {SessionPool.sessions.get(sessionId).getAsyncRemote().sendText(message);}}//点对点的消息推送public static void sendMessage(Map<String, Object> params) {String userId = params.get("formUserId").toString();String toUserId = params.get("toUserId").toString();String msg = params.get("message").toString();//获取用户sessionSession session = sessions.get(toUserId);//session不为空的情况下进行点对点推送if (session != null) {session.getAsyncRemote().sendText(msg);}}
}

代码部分功能分析

重连机制

当当前页面的websocket与后端链接断开时,或报错或链接的时候遇到问题,没有连接成功,如果没有重连机制,那么客户端和服务器的交互到此结束,但是事实上并非如此,如果服务器又恢复正常了,能连接上了呢,前端只能刷新页面才能连上,重连机制就是在服务器断开后不停进行重连的操作。

呈现的效果:

  • 连接失败时进行重连

  • 连接成功时

心跳机制

当客户端和浏览器之间的网络连接中断,客户端和服务器都无法发现报错,但是此时客户端和服务器已经不能通信,添加心跳机制能很好的避免这个问题,和重连机制类似,每隔几秒钟向服务器发送一次消息,服务器能给出答复说明他们之间连接没问题,否则进行重连

心跳重连一般添加到链接后和发消息后:

显示查看心跳机制:

写在最后

第一次接触websocket如果代码中存在问题请指出!!

微信小程序结合SpringBoot实现WebSocket长链接相关推荐

  1. 基于微信小程序的springboot客运汽车票购票系统源码和论文

    在客运公司工作 7 年之余,对客运管理的难度深有感触.特别是在春运期 间购票难依旧是长途汽车订票的一大难题.长途汽车和火车的订票管理虽然有 差异,但大体上是相同的.长途汽车在售票的过程中需要对旅客的起 ...

  2. 微信小程序以SpringBoot作为后端开发遇到的问题及解决思路总结

    背景 关键字:微信小程序.SpringBoot 项目.腾讯云服务器.SSL.HTTPS.Tomcat 注:本文记录了一个菜鸟解决问题的思路,觉得啰嗦可以略过,解决方案在文末.技术不精,文章有错误烦请指 ...

  3. 微信小程序实现点击拍照长按录像功能

    微信小程序实现点击拍照长按录像功能 代码里面注释写的都很详细,直接上代码.官方的组件属性中有触摸开始和触摸结束属性.本功能依靠这些属性实现. .wxml代码: <!-- 相机 pages/cam ...

  4. docker + nginx +微信小程序后台 (springboot)

    1.资源环境 阿里云ecs服务器.centos8 系统 . docker 2.0 .nginx .微信小程序. springboot jar包 .ssl证书. 已备案的域名 www.dan-huang ...

  5. 【uni-app】UniApp实现微信小程序中拨打手机电话和长按加微信客服好友(完整代码示例)

    UniApp实现微信小程序中拨打手机电话和长按加微信客服好友(完整代码示例) 一.service.Vue <template><view><!-- 标题栏 -->& ...

  6. 基于微信小程序+VUE+Springboot+Mysql的中小学生作业管理系统

    基于微信小程序+VUE+Springboot+Mysql的中小学生作业管理系统 ✌全网粉丝20W+,csdn特邀作者.博客专家.CSDN新星计划导师.java领域优质创作者,博客之星.掘金/华为云/阿 ...

  7. 微信小程序公众平台的文档链接

    微信小程序公众平台的文档链接:https://mp.weixin.qq.com/debug/wxadoc/dev/api/network-file.html

  8. filezilla 设置服务器_java项目部署到linux服务器,微信小程序后台springboot项目部署到腾讯云服务器(图文详解)...

    前面给大家讲了一个点餐系统的开发,包括java点餐后台和微信点餐小程序.可是都是教大家如何在本地把项目跑起来.今天就来教大家如何把这个点餐系统部署到服务器,实现商用. 传送门 点餐系统的开发,java ...

  9. springboot 获取登录浏览器_java项目部署到linux服务器,微信小程序后台springboot项目部署到云服务器(图文详解)...

    前面给大家讲了一个点餐系统的开发,包括java点餐后台和微信点餐小程序.可是都是教大家如何在本地把项目跑起来.今天就来教大家如何把这个点餐系统部署到服务器,实现商用. 传送门 点餐系统的开发,java ...

最新文章

  1. Python-100 练习题 01 列表推导式
  2. Coreseek:indexer crashed神秘
  3. 在ubuntu16.04中安装apache2+modsecurity以及自定义WAF规则详解
  4. Git/小白从零开始篇
  5. ftp连接 java.net.ConnectException: Connection refused
  6. 使用Docker-容器命令案例1
  7. Python回调函数的实现
  8. 【javascript】js检验注册密码强度效果
  9. Python绘制分形树(一)
  10. 基于 iso 镜像构建 yum 本地源
  11. 尚硅谷python101_04_尚硅谷大数据之Zookeeper实战
  12. 项目启动报 JDBC Driver has been forcibly unregistered
  13. AD中如何进行logo的自制与导入
  14. GDScript:关于派生类调用基类方法的一个注意事项
  15. Ubuntu下的MySQL数据库
  16. “差不多先生”引发的悲剧
  17. Recurrent Filter Learning for Visual Tracking(RFL)论文笔记
  18. Zabbix 配置钉钉告警
  19. da2 Android版本固件,OPPOAce2官方系统刷机包(完整固件最新升级包ColorOS 7)
  20. 给windows电脑重装系统

热门文章

  1. Northwind and pubs Sample Databases for SQL Server 2000
  2. 欧美国家ADS-B设备装配进展
  3. 【C语言】测试4 循环程序设计
  4. 前端设计 最详细的div介绍+效果图+代码图
  5. Python3.7版---双人联机雷霆战机(2D特效+音效+道具+Linux系统)
  6. AI 激发绘画灵感的 N 种可能 | MidJourney
  7. el-table使用lazy-tree模式,数据重载节点中的tree数据不会更新的问题
  8. 十大MySQL开发工具
  9. vue3.0找不到模块“./App.vue”或其相应的类型声明。
  10. h5上传图片的两种方法