导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>3.0.4</version>
</dependency>
创建一个WebSocketConfig类
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
创建一个WebSocketServer类
package com.czf.study.wevSocket;
import lombok.extern.slf4j.Slf4j;
import org.junit.platform.commons.util.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.concurrent.ConcurrentHashMap;
@ServerEndpoint("/websocket/{userId}")
@Component
@Slf4j
public class WebSocketServer {
/**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
private static int onlineCount = 0;
/**concurrent包的线程安全集合,也可以map改成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.put(userId,this);
//在线数加1
addOnlineCount();
}
log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount());
try {
sendMessage("连接成功");
} catch (IOException e) {
log.error("用户:"+userId+",网络异常!!!!!!");
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
//从集合中删除
subOnlineCount();
}
log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("【websocket消息】收到客户端发来的消息:{}", message);
}
/**
*
* @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 {
this.session.getBasicRemote().sendText(message);
}
/**
* 发送自定义消息
* */
public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
log.info("发送消息到:"+userId+",报文:"+message);
if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
webSocketMap.get(userId).sendMessage(message);
}else{
log.error("用户"+userId+",不在线!");
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
服务端发请求接口
外面创建一个接口,模拟服务端发请求给客户端
@RestController
public class DemoController {
@RequestMapping("/push/{toUserId}")
public ResponseEntity<String> pushToWeb(String message, @PathVariable String toUserId) throws IOException {
WebSocketServer.sendInfo(message,toUserId);
return ResponseEntity.ok("MSG SEND SUCCESS");
}
}
测试



此时控制台输出
2022-11-15 11:51:43.009 INFO 28972 --- [nio-8787-exec-5] com.czf.study.wevSocket.WebSocketServer : 用户连接:1,当前在线人数为:1
接下来,模拟服务端给客户端发送请求,建立一个http请求


控制台输出
![]()
客户端收到请求

双人聊天室
webSocket经常被用作聊天室,两个客户端,通过一个服务端分发请求,进行沟通。
在此案例中,通过
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("【websocket消息】收到客户端发来的消息:{}", message);
String[] split = message.split("/");
try {
sendInfo(split[0],split[1]);
} catch (IOException e) {
e.printStackTrace();
}
}
比如


前台测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<title>websocket测试页面</title>
</head>
<body>
<div class="panel panel-default">
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<div class="input-group">
<span class="input-group-addon">ws地址</span>
<input type="text" id="address" class="form-control" placeholder="ws地址"
aria-describedby="basic-addon1" value="ws://localhost:9700/tunnel/websocket">
<div class="input-group-btn">
<button class="btn btn-default" type="submit" id="connect">连接</button>
</div>
</div>
</div>
</div>
<div class="row" style="margin-top: 10px;display: none;" id="msg-panel">
<div class="col-md-6">
<div class="input-group">
<span class="input-group-addon">消息</span>
<input type="text" id="msg" class="form-control" placeholder="消息内容" aria-describedby="basic-addon1">
<div class="input-group-btn">
<button class="btn btn-default" type="submit" id="send">发送</button>
</div>
</div>
</div>
</div>
<div class="row" style="margin-top: 10px; padding: 10px;">
<div class="panel panel-default">
<div class="panel-body" id="log" style="height: 450px;overflow-y: auto;">
</div>
</div>
</div>
</div>
</div>
<script src="//i2.wp.com/cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
<script src="//i2.wp.com/cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"></script>
<script type="text/javascript">
$(function () {
var _socket;
$("#connect").click(function () {
var tunnelId = "2"; // 设置需要传递的参数
_socket = new _websocket($("#address").val(), tunnelId);
_socket.init();
});
$("#send").click(function () {
var _msg = $("#msg").val();
output("发送消息:" + _msg);
_socket.client.send(_msg);
});
});
function output(e) {
var _text = $("#log").html();
$("#log").html(_text + "<br>" + e);
}
function _websocket(address, tunnelId) {
this.address = address;
this.tunnelId = tunnelId;
console.log(address)
console.log(tunnelId)
this.client;
this.init = function () {
if (!window.WebSocket) {
this.websocket = null;
return;
}
var _this = this;
// var _client = new window.WebSocket(_this.address + "/" + _this.tunnelId);// 路径传参(没跑通)
// 注意这里的名字要和后端接参数的名字对应上
var _client = new window.WebSocket(_this.address + "?tunnelId=" + tunnelId);
_client.onopen = function () {
output("websocket打开");
$("#msg-panel").show();
};
_client.onclose = function () {
_this.client = null;
output("websocket关闭");
$("#msg-panel").hide();
};
_client.onmessage = function (evt) {
output(evt.data);
};
_this.client = _client;
};
return this;
}
</script>
</body>
</html>

