WebSocket 是一种在客户端和服务器之间提供低延迟、全双工通信的网络协议。它允许双方在建立一次连接后,进行实时、持续的数据交换,无需像HTTP那样为每一个请求和响应建立新的连接。WebSocket的设计初衷是解决传统HTTP协议在实时通信方面的不足,比如实现实时聊天、游戏、股票报价等需要高频率、连续数据更新的应用场景。
1 构建服务端
1.1 引入依赖
| <dependency> |
| <groupId>org.springframework.boot</groupId> |
| <artifactId>spring-boot-starter-websocket</artifactId> |
| </dependency> |
1.2 创建配置类
| @Slf4j |
| @Configuration |
| public class WebSocketConfig { |
| @Bean |
| public ServerEndpointExporter serverEndpointExporter() { |
| return new ServerEndpointExporter(); |
| } |
| } |
1.3 创建服务端
| import lombok.extern.slf4j.Slf4j; |
| import org.springframework.stereotype.Component; |
| |
| import javax.websocket.OnClose; |
| import javax.websocket.OnMessage; |
| import javax.websocket.OnOpen; |
| import javax.websocket.Session; |
| import javax.websocket.server.PathParam; |
| import javax.websocket.server.ServerEndpoint; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.concurrent.CopyOnWriteArraySet; |
| |
| @Slf4j |
| @Component |
| @ServerEndpoint("/websocket/{userId}") |
| public class WebSocketTest { |
| |
| |
| |
| private static final CopyOnWriteArraySet<Session> SESSIONS = new CopyOnWriteArraySet<>(); |
| |
| |
| |
| |
| private static final Map<String, Session> SESSION_POOL = new HashMap<>(); |
| |
| @OnOpen |
| public void onOpen(Session session, @PathParam(value = "userId") String userId) { |
| try { |
| SESSIONS.add(session); |
| SESSION_POOL.put(userId, session); |
| log.info("【WebSocket消息】有新的连接,总数为:" + SESSIONS.size()); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| @OnClose |
| public void onClose(Session session) { |
| try { |
| SESSIONS.remove(session); |
| log.info("【WebSocket消息】连接断开,总数为:" + SESSIONS.size()); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| @OnMessage |
| public void onMessage(String message) { |
| log.info("【WebSocket消息】收到客户端消息:" + message); |
| Session session = SESSION_POOL.get("123"); |
| if (session != null && session.isOpen()) { |
| try { |
| synchronized (session) { |
| log.info("【WebSocket消息】单点消息:" + message); |
| session.getAsyncRemote().sendText(message); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| public void sendAllMessage(String message) { |
| log.info("【WebSocket消息】广播消息:" + message); |
| for (Session session : SESSIONS) { |
| try { |
| if (session.isOpen()) { |
| session.getAsyncRemote().sendText(message); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| public void sendOneMessage(String userId, String message) { |
| Session session = SESSION_POOL.get(userId); |
| if (session != null && session.isOpen()) { |
| try { |
| synchronized (session) { |
| log.info("【WebSocket消息】单点消息:" + message); |
| session.getAsyncRemote().sendText(message); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| public void sendMoreMessage(String[] userIds, String message) { |
| for (String userId : userIds) { |
| Session session = SESSION_POOL.get(userId); |
| if (session != null && session.isOpen()) { |
| try { |
| log.info("【WebSocket消息】单点消息:" + message); |
| session.getAsyncRemote().sendText(message); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| } |
2 创建客户端
2.1 引入依赖
| <dependency> |
| <groupId>org.java-websocket</groupId> |
| <artifactId>Java-WebSocket</artifactId> |
| <version>1.5.3</version> |
| </dependency> |
2.2 注入对象
在配置类中注入WebSocketClient
| import lombok.extern.slf4j.Slf4j; |
| import org.java_websocket.client.WebSocketClient; |
| import org.java_websocket.handshake.ServerHandshake; |
| import org.springframework.context.annotation.Bean; |
| import org.springframework.context.annotation.Configuration; |
| import org.springframework.web.socket.server.standard.ServerEndpointExporter; |
| |
| import java.net.URI; |
| |
| @Slf4j |
| @Configuration |
| public class WebSocketConfig { |
| @Bean |
| public ServerEndpointExporter serverEndpointExporter() { |
| return new ServerEndpointExporter(); |
| } |
| |
| @Bean |
| public WebSocketClient webSocketClient() { |
| WebSocketClient wsc = null; |
| try { |
| wsc = new WebSocketClient(new URI("ws://127.0.0.1:2580/websocket/123")) { |
| @Override |
| public void onOpen(ServerHandshake serverHandshake) { |
| log.info("与服务端建立连接"); |
| } |
| |
| @Override |
| public void onMessage(String s) { |
| log.info("收到服务端的消息:{}", s); |
| } |
| |
| @Override |
| public void onClose(int i, String s, boolean b) { |
| log.info("与服务端的连接断开 code:{} reason:{} {}", i, s, b); |
| } |
| |
| @Override |
| public void onError(Exception e) { |
| log.info("连接报错"); |
| } |
| }; |
| wsc.connect(); |
| return wsc; |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| return wsc; |
| } |
| } |
2.3 测试
| import org.java_websocket.client.WebSocketClient; |
| import org.springframework.web.bind.annotation.GetMapping; |
| import org.springframework.web.bind.annotation.RestController; |
| |
| import javax.annotation.Resource; |
| |
| @RestController |
| public class TestController { |
| |
| @Resource |
| WebSocketClient wsClient; |
| |
| @GetMapping("/send") |
| public void test(String text){ |
| wsClient.send(text); |
| } |
| } |
2.4 结果
浏览器访问
| http://localhost:2580/send?text=测试 |
结果
| 2024-07-06 15:38:28.586 INFO 25860 --- [nio-2580-exec-4] c.x.p.websocket.socket.WebSocketTest : 【WebSocket消息】收到客户端消息:测试 |
| 2024-07-06 15:38:28.586 INFO 25860 --- [nio-2580-exec-4] c.x.p.websocket.socket.WebSocketTest : 【WebSocket消息】单点消息:测试 |
| 2024-07-06 15:38:28.587 INFO 25860 --- [ctReadThread-31] c.x.p.websocket.config.WebSocketConfig : 收到服务端的消息:测试 |

【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战