Java实现Websocket通常有两种方式:
1、创建WebSocketServer类,里面包含open、close、message、error等方法;
2、利用Springboot提供的webSocketHandler类,创建其子类并重写方法。我们项目虽然使用Springboot框架,不过仍采用了第一种方法实现
(1)websocket依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
(2)创建配置类WebSocketConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
/**
* 如果使用Springboot默认内置的tomcat容器,则必须注入ServerEndpoint的bean;
* 如果使用外置的web容器,则不需要提供ServerEndpointExporter,下面的注入可以注解掉
*/
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
(3)创建webocketServer
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
* @ServerEndpoint 可以把当前类变成websocket服务类
*/
@Slf4j
@ServerEndpoint("/websocket/{userName}/{deptId}")
@Component
public class WebSocketServer {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userName")String userName,@PathParam("deptId")Integer deptId) throws IOException {
this.session = session;
//加入set中
webSocketSet.add(this);
//在线数加1
addOnlineCount();
~~业务~~
log.info("有新连接加入!当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(@PathParam("userName")String userName,@PathParam("deptId")Integer deptId){
//从set中删除
webSocketSet.remove(this);
//在线数减1
subOnlineCount();
~~业务~~
log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session,@PathParam("userName")String userName,@PathParam("deptId")Integer deptId) {
log.info("客户端发送的消息{},客户端连接个数,{}",message,onlineCount);
~~业务~~
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) throws IOException {
log.error("发生错误:{},SessionID: {}",error.getMessage(),session.getId());
this.session.getBasicRemote().sendText("error");
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
try {
log.info("后台向前台发送数据:{}",message);
this.session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("发送消息出错:{}", e.getMessage());
e.printStackTrace();
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
(4)服务端提供接口进行消息推送
import com.alibaba.fastjson.JSON;
import com.breezedi.common.core.controller.BaseController;
import com.breezedi.common.core.domain.AjaxResult;
import com.breezedi.common.utils.DateUtils;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 二维码相关
*/
@CrossOrigin
@RestController
@RequestMapping("/qrcode")
@Slf4j
public class QRCodeController extends BaseController {
//需要验证的用户保存到这里
public static Map<String,String> waitForUserValid = new HashMap<>();
//定时缓存用户数据==判斷60秒
public static Cache<String, Object> numCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
//定时缓存用户数据
public static Cache<String, Object> numCacheScan = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
//验证
public static Cache<String, Object> validation = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
//用户扫码购买取消长连接
public static Cache<String, Object> buyPkgValidation = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
/**
* 门槛验证时通过手机扫码
*/
@GetMapping(value = "/api/scan/{userName}/{deptId}/{cpId}/{userId}")
public AjaxResult scanChooseCpUser(@PathVariable("userName")String userName, @PathVariable("deptId")Long deptId,@PathVariable("cpId")Long cpId,@PathVariable("userId")Long userId,@RequestParam(required = false) Long pkgId,@RequestParam(required = false) Long detailId){
~~业务~~
~~if (waitForUserValid.containsKey(userName+"-"+deptId)){
waitForUserValid.put(userName+"-"+deptId,sb.toString());
}
numCacheScan.invalidate(userName+"-"+deptId);~~
return AjaxResult.success();
}
}
(5)客户端调用消息推送接口接受消息
客户端调用发送消息的链接: wss://域名+路径/websocket/参数/参数
getOrderList
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)title>
<script type="text/javascript">
function clock(){
console.log(1);
}
function WebSocketTest(){
if ("WebSocket" in window){
alert("您的浏览器支持 WebSocket!");
// 打开一个 web socket
var ws = new WebSocket(`ws://localhost:9083/admin/websocket/aaaa/103`);
ws.onopen = function(){
// Web Socket 已连接上,使用 send() 方法发送数据
ws.send("发送数据");
alert("数据发送中...");
self.setInterval(function (){
ws.send("help");
},1000);
};
ws.onmessage = function (evt){
var received_msg = evt.data;
console.log("前台接收到后台推送的数据:"+received_msg);
ws.close();
alert("数据已接收...");
};
ws.onclose = function(){
// 关闭 websocket
alert("连接已关闭...");
};
}
else{
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
}
}
script>
head>
<body>
<div id="sse">
<a href="javascript:WebSocketTest()">运行 WebSocketa>
div>
body>
html>
(6)nginx配置wss访问
server {
listen 443 ssl;
server_name ~~xxxxxxxxxxxx~~ ;
ssl_certificate ~~xxxxxxxxxxx~~ ;
ssl_certificate_key ~~xxxxxxxxxxxxxxx~~ ;
ssl_session_timeout 5m;
#websocket配置
location /admin/websocket { #注意路径要写对
proxy_pass http://127.0.0.1:9083/admin/websocket;
proxy_http_version 1.1;
proxy_connect_timeout 4s;
proxy_read_timeout 7200s;
proxy_send_timeout 12s;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}