1、引入pom文件
<!--WebSocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、新建WebSocketConfig配置类
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * @Explain: 开启webSocket 在线测试地址:http://www.jsons.cn/websocket/# */ @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
3、新建WebSocketServer服务类
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; 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.HashMap; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; @ServerEndpoint("/websocket/{userId}") @Component @Slf4j public class WebSocketServer { /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/ private static int onlineCount = 0; /**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/ private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>(); /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/ private Session session; /**接收userId*/ private String userId=""; /** * 连接建立成功调用的方法 * */ @OnOpen public void onOpen(Session session, @PathParam("userId") String userId) { this.session = session; this.userId=userId; if(webSocketMap.containsKey(userId)){ webSocketMap.remove(userId); //加入set中 }else{ webSocketMap.put(userId,this); //加入set中 addOnlineCount(); //在线数加1 } log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount()); try { HashMap<Object, Object> map = new HashMap<>(); map.put("key","连接成功"); sendMessage(JSON.toJSONString(map)); } catch (IOException e) { log.error("用户:"+userId+",网络异常!!!!!!"); } } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { if(webSocketMap.containsKey(userId)){ webSocketMap.remove(userId); //从set中删除 subOnlineCount(); } log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息*/ @OnMessage public void onMessage(String message, Session session) { log.info("用户消息:"+userId+",报文:"+message); //可以群发消息 //消息保存到数据库、redis if(StringUtils.isNotBlank(message)){ try { //解析发送的报文 JSONObject jsonObject = JSONObject.parseObject(message); //追加发送人(防止串改) jsonObject.put("fromUserId",this.userId); String fromUserId=jsonObject.getString("fromUserId"); //传送给对应toUserId用户的websocket if(StringUtils.isNotBlank(fromUserId) && webSocketMap.containsKey(fromUserId)){ webSocketMap.get(fromUserId).sendMessage(jsonObject.toJSONString()); //自定义-业务处理 // DeviceLocalThread.paramData.put(jsonObject.getString("group"),jsonObject.toJSONString()); }else{ log.error("请求的userId:"+fromUserId+"不在该服务器上"); //否则不在这个服务器上,发送到mysql或者redis } }catch (Exception e){ e.printStackTrace(); } } } /** * 发生错误时候 * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { log.error("用户错误:"+this.userId+",原因:"+error.getMessage()); error.printStackTrace(); } /** * 实现服务器主动推送 */ public void sendMessage(String message) throws IOException { //加入线程锁 synchronized (session){ try { //同步发送信息 this.session.getBasicRemote().sendText(message); } catch (IOException e) { log.error("服务器推送失败:"+e.getMessage()); } } } /** * 发送自定义消息 * */ /** * 发送自定义消息 * @param message 发送的信息 * @param toUserId 如果为null默认发送所有 * @throws IOException */ public static void sendInfo(String message,String toUserId) throws IOException { //如果userId为空,向所有群体发送 if(StringUtils.isEmpty(toUserId)) { //向所有用户发送信息 Iterator<String> itera = webSocketMap.keySet().iterator(); while (itera.hasNext()) { String keys = itera.next(); WebSocketServer item = webSocketMap.get(keys); item.sendMessage(message); } } //如果不为空,则发送指定用户信息 else if(webSocketMap.containsKey(toUserId)){ WebSocketServer item = webSocketMap.get(toUserId); item.sendMessage(message); }else{ log.error("请求的userId:"+toUserId+"不在该服务器上"); } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; } public static synchronized ConcurrentHashMap<String, WebSocketServer> getWebSocketMap(){ return WebSocketServer.webSocketMap; }
4、修改SecurityConfig配置类,系统中使用权限框架时,需要对端点进行放行。
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
httpSecurity.authorizeRequests().antMatchers("/websocket/**").permitAll();
}
5、使用连接工具测试
websocket在线调试网址:
6、前端页面websocket.vue
<template>
<div class="app-container home">
<el-row :gutter="20">
<el-col :sm="24" :lg="24">
<h1>集成websocket测试</h1>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :sm="24" :lg="24">
<div>
<el-input v-model="url" type="text" style="width: 20%"/>
<el-button @click="join" type="primary">连接</el-button>
<el-button @click="exit" type="danger">断开</el-button>
<br/>
<br/>
<el-input type="textarea" v-model="message" :rows="9"/>
<br/>
<br/>
<el-button type="info" @click="send">发送消息</el-button>
<br/>
<br/>
<el-input type="textarea" v-model="text_content" :rows="9"/>
返回内容
<br/>
<br/>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "Index",
data() {
return {
url: "ws://127.0.0.1:8080/websocket/1",
message: "",
text_content: "",
ws: null,
};
},
methods: {
join() {
const wsuri = this.url;
this.ws = new WebSocket(wsuri);
const self = this;
this.ws.onopen = function (event) {
self.text_content = self.text_content + "已经打开连接!" + "\n";
};
this.ws.onmessage = function (event) {
self.text_content = event.data + "\n";
};
this.ws.onclose = function (event) {
self.text_content = self.text_content + "已经关闭连接!" + "\n";
};
},
exit() {
if (this.ws) {
this.ws.close();
this.ws = null;
}
},
send() {
if (this.ws) {
this.ws.send(this.message);
} else {
alert("未连接到服务器");
}
},
},
};
</script>