springboot实现websocket通信

1.主要简单记录项目中需要用到和websocket进行通信,然后写的一个小的代码案例。
websocket是一种在单个 TCP 连接上进行全双工通信的协议,它一个长连接,在客户端和服务器之间保持持久的连接,从而可以实时地发送和接收数据。

2.首先引入所需要的依赖包

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-messaging</artifactId>
		</dependency>

3.在服务端建立MyWebSocketHandler类继承socket通信类重写其方法,handleTextMessage方法和handleBinaryMessage都是用来接收消息的返回方法,二者选其一用就行了 主要是接收的参数类型不同。

package com.analysismanage.websocket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.adapter.standard.StandardWebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class MyWebSocketHandler extends TextWebSocketHandler {
    private static final Logger logger = LoggerFactory.getLogger(MyWebSocketHandler.class);

    private static final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        URI uri = session.getUri();
        // 提取查询参数
        String query = uri.getQuery();  // 获取 query string: token=${token}&id=${id}

        if (query != null) {
            Map<String, String> params = parseQueryParams(query);
            String id = params.get("doc");
            String username = params.get("userName");  // 假设你存储的是用户名
            System.out.println("ID: " + id);

            session.getAttributes().put("id", id);
            session.getAttributes().put("username", username);
            sessions.put(session.getId(), session);
        }else {

            sessions.put(session.getId(), session);
        }
        System.out.println("WebSocket连接已建立,当前用户数:" + sessions.size());
        System.out.println("WebSocket连接已建立");
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
//        // 收到消息时调用
//        String payload = message.getPayload();
//
//        // 假设消息是8位二进制编码的字符串,使用Base64解码
//        byte[] decodedBytes = Base64Utils.decodeFromString(payload);
//
//        // 将字节数组转换为字符串,注意字符集
//        String decodedPayload = new String(decodedBytes, "UTF-8");
//
//        System.out.println("接收到消息:" + decodedPayload);
//
//        // 处理消息并发送响应
//        session.sendMessage(new TextMessage("收到消息:" + decodedPayload));
    }

    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
        System.out.println("接收到消息:" + message);
        try {
        	//获取接收到的二进制数据转byte
            ByteBuffer byteBuffer = message.getPayload();
            byte[] bytes = new byte[byteBuffer.remaining()];
            byteBuffer.get(bytes);

            // 打印字节数组的十六进制内容
//            StringBuilder hexString = new StringBuilder();
//            for (byte b : bytes) {
//                hexString.append(String.format("%02X ", b));  // 输出每个字节的十六进制表示
//            }
//            System.out.println("接收到消息的十六进制内容: " + hexString.toString().trim());
//
//            try {
//                String messageString = new String(bytes, "UTF-8");
//                System.out.println("接收到的消息(UTF-8 解码): " + messageString);
//            } catch (UnsupportedEncodingException e) {
//                System.out.println("接收到的消息不是有效的 UTF-8 编码");
//            }

			//判断socket会话是否关闭 关闭则不进行发送数据操作
            if (session.isOpen() && session instanceof StandardWebSocketSession) {
                StandardWebSocketSession standardSession = (StandardWebSocketSession) session;
                if (standardSession.isOpen() && standardSession.getRemoteAddress() != null) {

                    // 获取文档ID 给相应文档的在线用户推送消息
                    URI uri = session.getUri();
                    String query = uri.getQuery();
                    Map<String, String> params = parseQueryParams(query);
                    String doc = params.get("doc");

                    // 发送二进制消息给所有在线用户
                    getSessionsById(sessions, doc).forEach(wsSession -> {
                        try {
                            // 检查 WebSocket session 是否处于有效状态
                            if (wsSession.isOpen()) {
                                synchronized (wsSession) {
                                    wsSession.sendMessage(new BinaryMessage(bytes));
                                    logger.info("Successfully sent binary message to: " + wsSession.getId());
                                }
                            } else {
                                logger.warn("WebSocket session is closed or unavailable.");
                            }
                        } catch (IOException e) {
                            logger.error("Failed to send binary message", e);
                        }
                    });
                } else {
                    // 如果会话不可用或关闭,处理这个情况
                    logger.error("WebSocket session is closed or unavailable.");
                }
            } else {
                logger.warn("Session is not an instance of StandardWebSocketSession or session is closed.");
            }
        } catch (Exception e) {
            // 捕获所有异常
            logger.error("Unexpected error occurred while handling binary message:", e);
            // 关闭会话或其他处理措施
            try {
                session.close(CloseStatus.SERVER_ERROR);
            } catch (IOException closeEx) {
                logger.error("Failed to close session after unexpected error: ", closeEx);
            }
        }
    }



    // 根据 ID 查找所有符合条件的 WebSocketSession
    public static List<WebSocketSession> getSessionsById(Map<String, WebSocketSession> sessions, String targetId) {
        List<WebSocketSession> matchingSessions = new ArrayList<>();

        // 遍历所有的 WebSocketSession
        for (WebSocketSession session : sessions.values()) {
            // 获取 session 的属性 (Map<String, Object>)
            Object id = session.getAttributes().get("id");  // 获取 ID 属性

            // 如果属性存在且 ID 匹配目标 ID,添加到结果列表中
            if (id != null && id.equals(targetId)) {
                matchingSessions.add(session);
            }
        }
        return matchingSessions;  // 返回所有符合条件的 WebSocketSession
    }


    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("WebSocket连接已关闭");
        sessions.remove(session.getId());
        System.out.println("WebSocket连接已关闭,当前用户数:" + sessions.size());
    }


    private Map<String, String> parseQueryParams(String query) {
        Map<String, String> paramMap = new HashMap<>();
        String[] pairs = query.split("&");  // 分割查询字符串成键值对
        for (String pair : pairs) {
            String[] keyValue = pair.split("=");  // 分割每个键值对
            if (keyValue.length == 2) {
                try {
                    String key = URLDecoder.decode(keyValue[0], "UTF-8");  // 解码键
                    String value = URLDecoder.decode(keyValue[1], "UTF-8");  // 解码值
                    paramMap.put(key, value);  // 存储键值对到 Map
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();  // 如果编码错误,处理异常
                }
            }
        }
        return paramMap;  // 返回解析后的 Map
    }
}

4.创建其配置类,使用EnableWebSocket注解注入socket对象到容器里面建立连接通信

package com.analysismanage.websocket;

import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.config.annotation.*;
import org.springframework.web.util.WebAppRootListener;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

@Component
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer, ServletContextInitializer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new MyWebSocketHandler(), "/my-websocket2")
//                .setAllowedOrigins("http://192.168.8.135", "http://example.com");
                .setAllowedOrigins("*");
    }

//重写这个方法设置传输数据大小 这里设置的50MB
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        servletContext.addListener(WebAppRootListener.class);
        servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize","52428800");
        servletContext.setInitParameter("org.apache.tomcat.websocket.binaryBufferSize","52428800");
    }
}

我上面使用的设置大小有其他更简单的方法进行设置,但是需要更高版本的框架依赖。

5.然后启动服务器即可,客户端就可以直接访问了 ws://localhost:8080/…
可以自定义选择工具进行测试哈。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值