document.write("");

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>
复制代码

 

posted @   人间春风意  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示

距今时间:
1018天1.00 小时 38.13 分钟

当前新增阅读数:139507