java SSM框架搭建微信小程序webSocket长连接(webSocket传参)

最近的项目有个功能点: 商户端需要扫描用户端的二维码, 扫描结果要展示给商户和用户端.

商户端的提示比较好处理, 根据接口返回数据进行展示就可以, 稍微麻烦的是用户被扫的提示.

解决方案有两种:

1.用户端进行循环查询,每2秒进行一次接口查询,接口有数据时,根据数据展示;

2.用户端使用webSocket与服务器进行长连接,有返回数据时再进行提示.

两种方式都有实现,简单说明一下1方式的实现, 本篇着重介绍2方式的实现

  • 轮询实现方式
  1. 商户扫码,判断二维码信息的合法性, 合法进行扣款;
  2. 扣款的同时在redis里存储一条记录, key是用户的标识, value是需要返回给用户的消息;
  3. 用户端轮询redis中的数据, 返回数据时提示用户扣款结果.

这种方式对服务器的资源占用较大(仅做说明)

  • webSocket实现方式
  1. 用户进入二维码扫码界面,与服务器建立webSocket连接, 服务端用map存储, key: 用户标识 value: session;
  2. 商户扫码后, 在map中查找用户的session, 向用户发送消息;
  3. 前端接收到webSocket的信息后, 进行相应的展示.

nginx配置

小程序的webSocket是https协议, 我们项目使用的是nginx转发, 需要配置nginx, 贴一下nginx配置,

如果不配置nginx的upgrade协议, wss请求会报错:Error during WebSocket handshake: Unexpected response code: 400

location /webSocket/ {} 中的webSocket根据自己websocket的注解进行更改


  1. map $http_upgrade $connection_upgrade {
  2. default upgrade;
  3. '' close;
  4. }
  5. server {
  6. listen 443 ssl;
  7. server_name 你的域名;
  8. ssl on;
  9. ssl_certificate cert/你的ssl pem.pem;
  10. ssl_certificate_key cert/你的ssl key.key;
  11. ssl_session_timeout 5m;
  12. ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
  13. ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  14. ssl_prefer_server_ciphers on;
  15. location / {
  16. proxy_pass http://127.0.0.1:8089;
  17. }
  18. location /webSocket/ {
  19. proxy_pass http://127.0.0.1:8089;
  20. proxy_http_version 1.1;
  21. proxy_set_header Upgrade $http_upgrade;
  22. proxy_set_header Connection $connection_upgrade;
  23. }
  24. }

maven依赖


  1. <dependency>
  2. <groupId>javax</groupId>
  3. <artifactId>javaee-api</artifactId>
  4. <version>7.0</version>
  5. <scope>provided</scope>
  6. </dependency>

webSocket 文件

webSocket的路径中包含了用户标识{userId}和{carwashId}, 这个方法也是借鉴大神的,没有找到出处了, 就没注明啦


  1. import com.carwash.util.ValidatorUtil;
  2. import javax.websocket.*;
  3. import javax.websocket.server.PathParam;
  4. import javax.websocket.server.ServerEndpoint;
  5. import java.io.IOException;
  6. import java.util.concurrent.ConcurrentHashMap;
  7. /**
  8. * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
  9. * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
  10. */
  11. @ServerEndpoint("/webSocket/{userId}/{carwashId}")
  12. public class WebSocketTest {
  13. //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
  14. private static int onlineCount = 0;
  15. //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
  16. private static ConcurrentHashMap<String, WebSocketTest> webSocketSet = new ConcurrentHashMap<String, WebSocketTest>();
  17. //与某个客户端的连接会话,需要通过它来给客户端发送数据
  18. private Session session;
  19. private String userIdCarwashId;
  20. /**
  21. * 连接建立成功调用的方法
  22. * @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
  23. */
  24. @OnOpen
  25. public void onOpen(Session session, @PathParam("userId") Integer userId, @PathParam("carwashId") Integer carwashId){
  26. this.session = session;
  27. this.userIdCarwashId = userId + "_" + carwashId;
  28. webSocketSet.put(userIdCarwashId, this); //加入set中
  29. addOnlineCount(); //在线数加1
  30. System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
  31. }
  32. /**
  33. * 连接关闭调用的方法
  34. */
  35. @OnClose
  36. public void onClose(){
  37. webSocketSet.remove(this.userIdCarwashId); //从set中删除
  38. subOnlineCount(); //在线数减1
  39. System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
  40. }
  41. /**
  42. * 收到客户端消息后调用的方法
  43. * @param message 客户端发送过来的消息
  44. * @param session 可选的参数
  45. */
  46. @OnMessage
  47. public void onMessage(String message, Session session) {
  48. // System.out.println("来自客户端的消息:" + message);
  49. // //群发消息
  50. // for(WebSocketTest item: webSocketSet){
  51. // try {
  52. // item.sendMessage(message);
  53. // } catch (IOException e) {
  54. // e.printStackTrace();
  55. // continue;
  56. // }
  57. // }
  58. }
  59. /**
  60. * 发生错误时调用
  61. * @param session
  62. * @param error
  63. */
  64. @OnError
  65. public void onError(Session session, Throwable error){
  66. System.out.println("发生错误");
  67. error.printStackTrace();
  68. }
  69. /**
  70. * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
  71. * @param message
  72. * @throws IOException
  73. */
  74. public static void sendMessage(String userIdCarwashId, String message) throws IOException{
  75. System.out.println("当前在线用户数量: " + webSocketSet.size());
  76. WebSocketTest userSocket = webSocketSet.get(userIdCarwashId);
  77. if(ValidatorUtil.isNotNull(userSocket)) {
  78. userSocket.session.getBasicRemote().sendText(message);
  79. }
  80. //this.session.getAsyncRemote().sendText(message);
  81. }
  82. public static synchronized int getOnlineCount() {
  83. return onlineCount;
  84. }
  85. public static synchronized void addOnlineCount() {
  86. WebSocketTest.onlineCount++;
  87. }
  88. public static synchronized void subOnlineCount() {
  89. WebSocketTest.onlineCount--;
  90. }
  91. }

还需要在springMvc.xml中增加包扫描

<context:component-scan base-package="com.carwash.websocket" />

调用发送消息的方法为

WebSocketTest.sendMessage(QRUserId + "_" + QRCarwashId, "0_扣费成功_剩余次数: " + restNumber);


小程序调试

在这里贴一下小程序的调试代码

websocket.wxml


  1. <!--pages/websocket/websocket.wxml-->
  2. <view class="page">
  3. <view class="page__hd">
  4. </view>
  5. <view class="page__bd">
  6. <button bindtap="connectWebsocket" type="primary">连接websocket</button>
  7. </view>
  8. </view>

websocket.js


  1. // pages/websocket/websocket.js
  2. Page({
  3. /**
  4. * 页面的初始数据
  5. */
  6. data: {
  7. },
  8. connectWebsocket: function () {
  9. wx.connectSocket({
  10. url: 'wss://你的域名/webSocket/17/2',
  11. data: {
  12. x: '1',
  13. y: '22'
  14. },
  15. header: {
  16. 'content-type': 'application/json'
  17. },
  18. method: "GET"
  19. })
  20. wx.onSocketOpen(function (res) {
  21. console.log('WebSocket连接已打开!')
  22. })
  23. wx.onSocketError(function (res) {
  24. console.log(res)
  25. console.log('WebSocket连接打开失败,请检查!')
  26. })
  27. wx.onSocketMessage(function (res) {
  28. console.log('收到服务器内容:' + res.data)
  29. })
  30. }
  31. })

商户扫码后的结果

java SSM框架搭建微信小程序webSocket长连接(webSocket传参)

到这里就大功告成了, 如有疑问可以留言

相关推荐