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/…
可以自定义选择工具进行测试哈。