最近的项目有个功能点: 商户端需要扫描用户端的二维码, 扫描结果要展示给商户和用户端.
商户端的提示比较好处理, 根据接口返回数据进行展示就可以, 稍微麻烦的是用户被扫的提示.
解决方案有两种:
1.用户端进行循环查询,每2秒进行一次接口查询,接口有数据时,根据数据展示;
2.用户端使用webSocket与服务器进行长连接,有返回数据时再进行提示.
两种方式都有实现,简单说明一下1方式的实现, 本篇着重介绍2方式的实现
这种方式对服务器的资源占用较大(仅做说明)
nginx配置
小程序的webSocket是https协议, 我们项目使用的是nginx转发, 需要配置nginx, 贴一下nginx配置,
如果不配置nginx的upgrade协议, wss请求会报错:Error during WebSocket handshake: Unexpected response code: 400
location /webSocket/ {} 中的webSocket根据自己websocket的注解进行更改
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name 你的域名;
ssl on;
ssl_certificate cert/你的ssl pem.pem;
ssl_certificate_key cert/你的ssl key.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:8089;
}
location /webSocket/ {
proxy_pass http://127.0.0.1:8089;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
maven依赖
javax
javaee-api
7.0
provided
webSocket 文件
webSocket的路径中包含了用户标识{userId}和{carwashId}, 这个方法也是借鉴大神的,没有找到出处了, 就没注明啦
import com.carwash.util.ValidatorUtil;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint("/webSocket/{userId}/{carwashId}")
public class WebSocketTest {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static ConcurrentHashMap webSocketSet = new ConcurrentHashMap();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
private String userIdCarwashId;
/**
* 连接建立成功调用的方法
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") Integer userId, @PathParam("carwashId") Integer carwashId){
this.session = session;
this.userIdCarwashId = userId + "_" + carwashId;
webSocketSet.put(userIdCarwashId, this); //加入set中
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(){
webSocketSet.remove(this.userIdCarwashId); //从set中删除
subOnlineCount(); //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
// System.out.println("来自客户端的消息:" + message);
// //群发消息
// for(WebSocketTest item: webSocketSet){
// try {
// item.sendMessage(message);
// } catch (IOException e) {
// e.printStackTrace();
// continue;
// }
// }
}
/**
* 发生错误时调用
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
* @param message
* @throws IOException
*/
public static void sendMessage(String userIdCarwashId, String message) throws IOException{
System.out.println("当前在线用户数量: " + webSocketSet.size());
WebSocketTest userSocket = webSocketSet.get(userIdCarwashId);
if(ValidatorUtil.isNotNull(userSocket)) {
userSocket.session.getBasicRemote().sendText(message);
}
//this.session.getAsyncRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketTest.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketTest.onlineCount--;
}
}
还需要在springMvc.xml中增加包扫描
调用发送消息的方法为
WebSocketTest.sendMessage(QRUserId + "_" + QRCarwashId, "0_扣费成功_剩余次数: " + restNumber);
小程序调试
在这里贴一下小程序的调试代码
websocket.wxml
websocket.js
// pages/websocket/websocket.js
Page({
/**
* 页面的初始数据
*/
data: {
},
connectWebsocket: function () {
wx.connectSocket({
url: 'wss://你的域名/webSocket/17/2',
data: {
x: '1',
y: '22'
},
header: {
'content-type': 'application/json'
},
method: "GET"
})
wx.onSocketOpen(function (res) {
console.log('WebSocket连接已打开!')
})
wx.onSocketError(function (res) {
console.log(res)
console.log('WebSocket连接打开失败,请检查!')
})
wx.onSocketMessage(function (res) {
console.log('收到服务器内容:' + res.data)
})
}
})
商户扫码后的结果
到这里就大功告成了, 如有疑问可以留言或者私信我.