测试网站:http://www.websocket-test.com/
前端代码
<template> <div class="app switchDisabledClass"> <el-table style="100%;" :data="tableData" highlight-current-row border fit> <el-table-column type="index" align="center" label="序号" width="60"></el-table-column> <el-table-column prop="goodsIdentityCode" align="center" label="识别码" width="210"></el-table-column> <el-table-column prop="productName" align="center" label="产品名称" width="210"></el-table-column> <el-table-column prop="specification" align="center" label="规格型号" width="110"></el-table-column> <el-table-column prop="batchNumber" align="center" label="批号" width="180"></el-table-column> <el-table-column prop="expiryDate" align="center" label="有效期" width="180"></el-table-column> <el-table-column prop="productionDate" align="center" label="生产日期" width="180"></el-table-column> <el-table-column prop="manufacturerName" align="center" label="生产厂家" width="180"></el-table-column> </el-table> <br/> <el-button type="primary" @click="conSocket">连接WEBSOCKET</el-button> </div> </template> <script> export default { name: "demo", data() { return { tableData:[], socket:'' } }, //计算属性 computed: { }, //监控data中的数据变化 watch: {}, //方法集合 methods: { //websocket连接 conSocket(){ if ("WebSocket" in window){ alert("您的浏览器支持 WebSocket!"); // 创建一个 websocket this.socket = new WebSocket("ws://192.168.1.107:8034/socket/00149733FABB"); // 监听socket连接 this.socket.onopen = this.onOpen // 监听socket消息 this.socket.onmessage = this.onMessage this.socket.onclose = this.onClose } else { // 浏览器不支持 WebSocket alert("您的浏览器不支持 WebSocket!"); } }, onOpen(){ console.log("socket连接成功") this.socket.send('客户端连接成功') }, onMessage(evt){ this.tableData.push(JSON.parse(evt.data)) }, onClose(){ console.log('socket关闭') } }, //生命周期 - 创建完成(可以访问当前this实例) created() { }, //生命周期 - 挂载完成(可以访问DOM元素) mounted() { }, activated() { }, //如果页面有keep-alive缓存功能,这个函数会触发 } </script> <style lang='scss'> .app { height: 100%; 100%; user-select: none; .item { padding: 30px 0; } .seamless-warp { height: 100%; overflow: hidden; } } </style>
后端代码
--引入包
<dependency>
<!-- websocket -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
package com.example.datasourcedemo.config; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * @author luwl * @date 2021/11/26 17:38 */ @Slf4j @Configuration public class WebMvcConfig implements WebMvcConfigurer { //服务器支持跨域 @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "POST","OPTIONS") .allowedHeaders("*") .exposedHeaders("Access-Control-Allow-Headers", "Access-Control-Allow-Methods", "Access-Control-Allow-Origin", "Access-Control-Max-Age", "X-Frame-Options") .allowCredentials(false) .maxAge(3600); } /** * The bean shown in the preceding example registers any @ServerEndpoint * annotated beans with the underlying WebSocket container. When deployed to a * standalone servlet container, this role is performed by a servlet container * initializer, and the ServerEndpointExporter bean is not required. * * @return * 在Spring中可以直接使用Java WebSocket API来提供服务,如果使用内置的web容器,需要做的仅仅是需要在下面添加 */ /** 注入ServerEndpointExporter,这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint 。 * 要注意,如果使用独立的servlet容器,而不是直接使用springboot的内置容器,就不要注入ServerEndpointExporter,因为它将由容器自己提供和管理。*/ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
import cn.com.zhengya.framework.exception.ServiceException; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.servlet.server.Session; import org.springframework.stereotype.Component; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; /** * webSocket客户端 * @author luwl * @date 2021/1/13 10:54 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端, * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端 * {mac}是传递唯一标识的识别参数 */ @Slf4j @Component @ServerEndpoint("/socket/{mac}") public class WebSocketServer { /** * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 */ private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>(); /** * 存活的session集合(使用线程安全的map保存) */ private static Map<String, Session> livingSessions = new ConcurrentHashMap<>(); /** * 在线的连接对象 */ private static volatile int livingCount = 0; /** * 当前的session对象 */ private Session session; /** * 建立连接的回调方法 * * @param session 与客户端的WebSocket连接会话 * @param mac 硬件MAC地址 唯一 */ @OnOpen public void onOpen(Session session, @PathParam("mac") String mac) { if (!livingSessions.containsKey(mac)) { webSocketSet.add(this); livingSessions.put(mac, session); this.session = session; addLivingCount(); log.info(mac + " 进入连接,当前连接中数量为:" + getLivingCount()); } else { log.info(mac + "重新连接,当前连接中数量为:" + getLivingCount()); } } @OnMessage public void onMessage(String message, Session session, @PathParam("mac") String mac) { log.info(mac + " : " + message); sendMessageToAll(message); } @OnError public void onError(Session session, Throwable error) { log.info("发生错误"); log.error(error.getStackTrace() + ""); } @OnClose public void onClose(Session session, @PathParam("mac") String mac) { webSocketSet.remove(this); livingSessions.remove(mac); subLivingCount(); log.info(mac + " 关闭连接,当前连接中数量为:" + getLivingCount()); } /** * 单独发送消息到指定用户 * * @param message */ public void sendMessage(String mac, String message) { try { if (getLivingCount() != 0) { livingSessions.get(mac).getBasicRemote().sendText(message); } else { throw new ServiceException("当前无连接对象"); } } catch (IOException e) { log.info("sendMessage error", e); } } /** * 单独发送消息到当前用户 * * @param message */ public void sendMessage(String message) { try { synchronized (this) { this.session.getBasicRemote().sendText(message); } } catch (IOException e) { log.info("sendMessage error", e); } } /** * 群发消息 * * @param message */ public void sendMessageToAll(String message) { if (getLivingCount() != 0) { for (WebSocketServer server : webSocketSet) { server.sendMessage(message); } } else { throw new ServiceException("当前无连接对象"); } } public static synchronized int getLivingCount() { return livingCount; } public static synchronized void addLivingCount() { WebSocketServer.livingCount++; } public static synchronized void subLivingCount() { WebSocketServer.livingCount--; } /** * 获取所有连接中对象的标识 * * @return */ public List<String> getAllMac() { return new ArrayList<>(livingSessions.keySet()); } }
-发送消息
@Data @ApiModel("发送消息给指定的对象") public class SocketParam { @ApiModelProperty("发送对象标识(物理机器的MAC地址)") private String mac; @ApiModelProperty("消息") private String message; }
@Slf4j @RestController @RequestMapping("/socket/api") @Api(value = "socket服务", tags = "socket服务") public class SocketController { @Autowired private WebSocketServer webSocketServer; @PostMapping("/sendMessageToOne") @ApiOperation(value = "发送消息给单个通道") public Result<?> sendMessageToOne(@RequestBody SocketParam socketParam){ webSocketServer.sendMessage(socketParam.getMac(),socketParam.getMessage()); return Result.success(); } @PostMapping("/sendMessageToALL") @ApiOperation(value = "发送消息给所有通道") public Result<?> sendMessageToAll(@RequestParam String message){ webSocketServer.sendMessageToAll(message); return Result.success(); } @PostMapping("/getAllSession") @ApiOperation(value = "获取所有连接中通道") public Result<?> getAllSession(){ return Result.success(webSocketServer.getAllMac()); } }