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 {// 连接建立成功调用的方法@OnOpenpublic void onOpen(Session session) {System.out.println("Connected to client, session ID: " + session.getId());}// 连接关闭调用的方法@OnClosepublic void onClose() {System.out.println("Connection closed");}// 收到客户端消息后调用的方法@OnMessagepublic void onMessage(String message, Session session) {System.out.println("Message from client: " + message);// 回送消息session.getAsyncRemote().sendText("Hello, I got your message: " + message);}// 发生错误时调用@OnErrorpublic 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 {@Beanpublic 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<>();@OnOpenpublic void onOpen(@PathParam("uid") String uid, Session session) {// 将新建立的会话添加到webSocketMap中webSocketMap.put(uid, this);// 其他代码...}@OnClosepublic 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<>();@OnOpenpublic 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);}@OnClosepublic 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); // 参数决定是否中断正在执行的任务}// 其他代码...}// 收到客户端消息后调用的方法@OnMessagepublic void onMessage(String message, Session session) {logger.info("Message from client: " + message);// 回送消息session.getAsyncRemote().sendText("Hello, I got your message: " + message);}// 发生错误时调用@OnErrorpublic 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) 不会阻塞当前线程。