websocket 转发 websocket java springboot
websocket的写法有多种
一种A ws转发到B ws的示例
A ws:
import com.example.sound.service.WebSocketForwardTHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; @Slf4j @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(m(), "testWs/{xxId}/{username}").setAllowedOrigins("*"); } @Bean public WebSocketForwardTHandler m() { return new WebSocketForwardTHandler(); } }
import com.alibaba.fastjson.JSON; import com.example.sound.config.LocalSessionManager; import com.example.sound.config.RemoteClientManager; import lombok.extern.slf4j.Slf4j; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.socket.*; import org.springframework.web.socket.handler.TextWebSocketHandler; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.socket.client.WebSocketConnectionManager; import javax.net.ssl.*; import java.io.IOException; import java.net.Socket; import java.net.URI; import java.nio.ByteBuffer; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.time.Instant; import java.util.Objects; @Component @Slf4j public class WebSocketForwardTHandler extends TextWebSocketHandler { @Value("${application.url}") private String targetWebSocketUrl; // 转发的目的ws地址 private WebSocketConnectionManager connectionManager; @Autowired TestService testService; private Instant lastActivityTime; public WebSocketForwardTHandler() { } @Override public void afterConnectionEstablished(WebSocketSession session) throws IOException { String path = Objects.requireNonNull(session.getUri()).getPath(); String[] paths = path.split("/"); String username = paths[paths.length -2]; // 用户名 String xxId = paths[paths.length -1]; // xxId String url = testService.getNewsMeetingUrl(username, xxId); WebSocketHttpHeaders headers = new WebSocketHttpHeaders(); headers.set("wss_address",url); headers.set("xxId",xxId); headers.set("username",username); session.getAttributes().put("wss_address", url); session.getAttributes().put("username", username); session.getAttributes().put("xxId", xxId); TrustManager trustManager = new X509ExtendedTrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } }; try { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{trustManager}, null); SSLSocketFactory socketFactory = sslContext.getSocketFactory(); URI uri = URI.create(targetWebSocketUrl + "/" + username + "/" + xxId + "?wss_address=" + url); String originalSessionID = session.getId(); WebSocketClient webSocketClient = new WebSocketClient(uri) { @Override public void onOpen(ServerHandshake serverHandshake) { LocalSessionManager.add(session.getId(), session); log.info("remote ws 连接"); } @Override public void onMessage(String s) { WebSocketSession originalSession = null; try { originalSession = LocalSessionManager.get(originalSessionID); } catch (Exception e) { log.error("获取target session 异常:" + e.getMessage()); } if (originalSession != null && originalSession.isOpen()) { try { originalSession.sendMessage(new TextMessage(s)); } catch (IOException e) { throw new RuntimeException(e); } } // 自定义处理 } @Override public void onMessage(ByteBuffer s) { log.info("ByteBuffer onMessage: "); } @Override public void onClose(int i, String s, boolean b) { log.info("remote onClose: " + s); if (i== 1001) { try { session.sendMessage(new TextMessage("{xx}")); // 自定义处理 } catch (IOException ex) { throw new RuntimeException(ex); } } LocalSessionManager.removeAndClose(originalSessionID); } @Override public void onError(Exception e) { log.info("当前连接到目的ws连接 onError: " + e.getMessage(), e); e.printStackTrace(); try { session.sendMessage(new TextMessage("{xxx}")); // 自定义处理 } catch (IOException ex) { throw new RuntimeException(ex); } } }; webSocketClient.setSocketFactory(socketFactory); webSocketClient.connect(); RemoteClientManager.add(session.getId(), webSocketClient); } catch (Exception e) { log.error("连接远端 ws报错, ",e ); e.printStackTrace(); } } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException { WebSocketClient client = null; try { client = RemoteClientManager.get(session.getId()); } catch (Exception e) { log.error("获取target client 异常:" + e.getMessage()); } if (client != null && client.isOpen()) { client.send(message.getPayload()); } else { } } @Override protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) { WebSocketClient client = null; try { client = RemoteClientManager.get(session.getId()); } catch (Exception e) { log.error("获取target client 异常:" + e.getMessage()); } if (client != null && client.isOpen()) { client.send(message.getPayload()); } else { } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { super.afterConnectionClosed(session, status); try { log.info(" afterConnectionClosed 回调 " + JSON.toJSONString(status)); String wss_address = session.getAttributes().get("wss_address").toString(); String username = session.getAttributes().get("username").toString(); String xxId = session.getAttributes().get("xxId").toString(); log.info("客户端 与 当前服务器的连接 afterConnectionClosed"); testService.saveEndTimeRecord(wss_address, username); RemoteClientManager.removeAndClose(session.getId()); LocalSessionManager.removeAndClose(session.getId()); testService.stopNewsMeeting(xxId); } catch (Exception e) { log.error("客户端与当前服务器 ws断开连接处理异常", e); } } }
import lombok.extern.slf4j.Slf4j; import org.springframework.web.socket.WebSocketSession; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; // 客户端与当前服务器的连接 @Slf4j public class LocalSessionManager { /** * 保存连接 session 的地方 */ public static ConcurrentHashMap<String, WebSocketSession> SESSION_POOL = new ConcurrentHashMap<>(); /** * 添加 session * * @param key */ public static void add(String key, WebSocketSession session) { // 添加 session SESSION_POOL.put(key, session); } /** * 删除 session,会返回删除的 session * * @param key * @return */ public static WebSocketSession remove(String key) { // 删除 session return SESSION_POOL.remove(key); } /** * 删除并同步关闭连接 * * @param key */ public static void removeAndClose(String key) { WebSocketSession session = remove(key); if (session != null) { try { // 关闭连接 session.close(); } catch (IOException e) { // todo: 关闭出现异常处理 e.printStackTrace(); } } } /** * 获得 session * * @param key * @return */ public static WebSocketSession get(String key) { // 获得 session return SESSION_POOL.get(key); } }
package com.example.sound.config; import lombok.extern.slf4j.Slf4j; import org.java_websocket.client.WebSocketClient; import org.springframework.web.socket.WebSocketSession; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; // 当前服务器与的目的ws的连接 @Slf4j public class RemoteClientManager { /** * 保存连接 session 的地方 */ public static ConcurrentHashMap<String, WebSocketClient> SESSION_POOL = new ConcurrentHashMap<>(); /** * 添加 session * * @param key */ public static void add(String key, WebSocketClient session) { // 添加 session SESSION_POOL.put(key, session); } /** * 删除 session,会返回删除的 session * * @param key * @return */ public static WebSocketClient remove(String key) { // 删除 session return SESSION_POOL.remove(key); } /** * 删除并同步关闭连接 * * @param key */ public static void removeAndClose(String key) { WebSocketClient session = remove(key); if (session != null) { // 关闭连接 session.close(); } } /** * 获得 session * * @param key * @return */ public static WebSocketClient get(String key) { // 获得 session return SESSION_POOL.get(key); } }
B ws:
import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.time.LocalDateTime; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @ServerEndpoint("/websocket/{userId}/{xxId}") @Component @Slf4j public class WebSocketServer { /**静态变量,用来记录当前在线连接数**/ private static int onlineCount = 0; /**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/ static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>(); /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/ private Session session; /**接收userId*/ private String userId=""; private String xxId=""; String username = ""; private CServerUtil cServerUtil = new CServerUtil(); /** * 连接建立成功调用的方法 * */ @OnOpen public void onOpen(Session session, @PathParam("userId") String userId, @PathParam("xxId") String xxId) throws Exception { this.session = session; this.userId = userId; this.xxId = xxId; if(webSocketMap.containsKey(xxId)){ webSocketMap.remove(xxId); //加入set中 }else{ webSocketMap.put(xxId,this); //加入set中 addOnlineCount(); //在线数加1 } username = userId; log.info("用户连接:"+username + " userId: " + userId + " xxId: " + xxId +",当前在线人数为:" + getOnlineCount()); Map<String, List<String>> listMap = session.getRequestParameterMap(); String joinUrl = listMap.get("wss_address").get(0); log.info(joinUrl); // 自定义,是否需要连接C server try { cServerUtil.init(joinUrl, session); } catch (Exception e) { sendErrorInfoToInitiatorServer(session, "tag_xxx", "连接不成功"); } try { sendMessage("连接成功"); } catch (IOException e) { log.error("用户:" + userId + " xxId: " + xxId + ",网络异常!!!!!!"); } } /** * 连接关闭调用的方法 */ @OnClose public void onClose() throws Exception { if(webSocketMap.containsKey(xxId)){ webSocketMap.remove(xxId); //从set中删除 subOnlineCount(); } log.info("用户退出 username :"+username + " userId: " + userId + " xxId: " + xxId + " meetingurl : " + cServerUtil.getMeetingJoinUrl() +",当前在线人数为:" + getOnlineCount()); log.info("ws已关闭,需要关闭远端 ws流"); cServerUtil.close(); // 自定义,是否C连接关闭 } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息*/ @OnMessage public void onMessage(String message, Session session) throws Exception { //可以群发消息 log.info("用户文本消息:" + userId + " xxId: " + xxId +",报文:"+message); if ("{xxxx}".equals(message)) { // 自定义处理 log.info("发起方主动断开"); cServerUtil.close(); } MSG msg = new MSG(); // 自定义 msg.setValue(message); msg.setTime(LocalDateTime.now().toString()); log.info("回传到发起方 ws:" + JSON.toJSONString(msg)); sendMessage(JSON.toJSONString(msg)); } /** * 收到客户端消息后调用的方法 * * @param data 客户端发送过来的消息*/ @OnMessage public void onMessage(byte[] data, Session session) { try { cServerUtil.send(data); } catch (Exception e) { log.error("推送数据失败,报错: " + e.getMessage()); } } /** * 发生错误时候 * @param session * @param error */ @OnError public void onError(Session session, Throwable error) throws Exception { log.error("用户错误:"+this.userId + " xxId: " + this.xxId+",原因:"+error.getMessage()); // 自定义,是否需要主动关闭C连接 log.info("ws异常,需要关闭 ws流,发送信息给连接发起端"); sendErrorInfoToInitiatorServer(session, "tag_xxx", "ws异常,需要关闭 ws流 "); cServerUtil.close(); error.printStackTrace(); } public void sendErrorInfoToInitiatorServer(Session session, String msgTag, String logTag) { try { log.info(logTag + " --- start"); String text = "{xx}"; // 自定义处理 log.info(text); session.getBasicRemote().sendText(text); } catch (Exception e1) { log.error(logTag + " 报错:{}", e1.getMessage()); e1.printStackTrace(); } finally { log.info(logTag + "--- end"); } } /** * 实现服务器主动推送 */ 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); } } 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; } }
其它:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析