Spring Boot中使用Java API创建WebSocket
添加WebSocket的依赖项
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
创建WebSocket端点
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
@ServerEndpoint(value = "/websocket")
@Component
public class WebSocketServer {
// 连接建立成功调用的方法
@OnOpen
public void onOpen(Session session) {
System.out.println("Connected to client, session ID: " + session.getId());
}
// 连接关闭调用的方法
@OnClose
public void onClose() {
System.out.println("Connection closed");
}
// 收到客户端消息后调用的方法
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("Message from client: " + message);
// 回送消息
session.getAsyncRemote().sendText("Hello, I got your message: " + message);
}
// 发生错误时调用
@OnError
public void onError(Session session, Throwable error) {
System.out.println("Error occurred");
error.printStackTrace();
}
}
配置WebSocket
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
优化:支持用户UID来保存WebSocket会话
import javax.websocket.OnClose;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint(value = "/websocket/{uid}")
@Component
public class WebSocketServer {
// 使用ConcurrentHashMap来存储用户ID和WebSocket会话对象的映射。
private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(@PathParam("uid") String uid, Session session) {
// 将新建立的会话添加到webSocketMap中
webSocketMap.put(uid, this);
// 其他代码...
}
@OnClose
public void onClose(@PathParam("uid") String uid, Session session) {
// 当会话关闭时,从webSocketMap中移除该会话
webSocketMap.remove(uid);
// 其他代码...
}
// ...其它方法如onMessage等等
}
优化:建立webscoket连接后,每隔15秒定时发送消息
@ServerEndpoint(value = "/websocket/{uid}")
@Component
public class DevAlarmMessageHandleController {
private static final Logger logger = LoggerFactory.getLogger(DevAlarmMessageHandleController.class);
private static final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(10); // 支持多个任务并发执行
private Map<Session, Future<?>> futures = new ConcurrentHashMap<>();
// 使用ConcurrentHashMap来存储用户ID和WebSocket会话对象的映射。
private static ConcurrentHashMap<String, DevAlarmMessageHandleController> webSocketMap = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(@PathParam("uuid") String uuid, Session session) {
logger.info("uuid: {}, sessionId: {}" , uuid, session.getId());
// 将新建立的会话添加到webSocketMap中
webSocketMap.put(uuid, this);
// 调用 scheduleAtFixedRate方法时,返回一个Future对象
Future<?> future = scheduler.scheduleAtFixedRate(() -> sendMessage(session), 0, 15, TimeUnit.SECONDS);
futures.put(session, future);
}
@OnClose
public void onClose(@PathParam("uuid") String uuid, Session session) {
logger.info("uuid: {}, sessionId: {}" , uuid, session.getId());
// 当会话关闭时,从webSocketMap中移除该会话
webSocketMap.remove(uuid);
Future<?> future = futures.remove(session);
if (future != null) {
future.cancel(false); // 参数决定是否中断正在执行的任务
}
// 其他代码...
}
// 收到客户端消息后调用的方法
@OnMessage
public void onMessage(String message, Session session) {
logger.info("Message from client: " + message);
// 回送消息
session.getAsyncRemote().sendText("Hello, I got your message: " + message);
}
// 发生错误时调用
@OnError
public void onError(Session session, Throwable error) {
logger.info("Error occurred");
error.printStackTrace();
}
public void sendMessage(Session session){
session.getAsyncRemote().sendText("current: " + LocalDateTime.now());
}
}
问题
wss://127.0.0.1:8080/websocket/与ws://127.0.0.1:8080/websocket/的区别
区别在于它们使用的协议不同。“wss”代表WebSocket Secure,它是WebSocket的安全版本,意味着数据传输是加密的,类似于HTTPS在HTTP上增加了安全层。而“ws”则是普通的WebSocket协议,不提供加密,类似于HTTP。简单来说,使用“wss”协议可以更安全地保护数据传输不被监听和篡改。在生产环境中,建议使用“wss”协议以确保安全性。如果你的服务部署在了安全环境中,有时候也被称为是在使用WebSocket over TLS(传输层安全)。
getBasicRemote()和getAsyncRemote()的区别
getBasicRemote()
用 getBasicRemote().sendText(message) 发送消息时,这个调用是阻塞的。这意味着,在消息发送完成之前,代码的执行将会暂停。简单,但是当发送大量消息或者处理多个客户端时可能会影响性能。
getAsyncRemote()
使用 getAsyncRemote().sendText(message) 发送消息时,调用将立即返回,而消息的发送则由另一个线程异步处理。提高了消息发送的效率,允许应用程序同时执行其他任务。异步发送是处理高并发环境或发送大量消息时的理想选择,因为getAsyncRemote().sendText(message) 不会阻塞当前线程。