测试网站: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());
}
}