package com.tplink.cloud.demo.websocket_client;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.converter.ByteArrayMessageConverter;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.RestTemplateXhrTransport;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
/**
* Description of this file
*
* @author Li Tailong
* @version 1.0
* @since 2025/11/7
*/
// VMS配置类(存储vmsId和对应凭证)
class VMSConfigLocal {
private final String vmsId;
private final String cookie;
private final String csrfToken;
private final String userId;
public VMSConfigLocal(String vmsId, String cookie, String csrfToken, String userId) {
this.vmsId = vmsId;
this.cookie = cookie;
this.csrfToken = csrfToken;
this.userId = userId;
}
public String getVmsId() {
return vmsId;
}
public String getCookie() {
return cookie;
}
public String getCsrfToken() {
return csrfToken;
}
public String getUserId() {
return userId;
}
}
// HTTP请求工具类(支持多vmsId)
class HttpUtilForLocal {
private static final String BASE_URL_TEMPLATE =
"https://localhost:8081/api/v2/vms/%s/local/%s/sites/1707841329070081/demosend";
public static void sendDemoRequest(VMSConfigLocal config, String uuid) {
try {
String baseUrl = String.format(BASE_URL_TEMPLATE, config.getVmsId(), config.getUserId());
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Cookie", config.getCookie());
headers.set("CSRF-TOKEN", config.getCsrfToken());
Map<String, Object> body = new HashMap<>();
body.put("total", 50);
body.put("uuid", uuid);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.exchange(
baseUrl, HttpMethod.POST, request, String.class);
System.out.printf("HTTP请求 VMSID:%s UUID:%s 状态码:%s%n",
config.getVmsId(), uuid, response.getStatusCode());
} catch (Exception e) {
System.err.printf("HTTP请求失败 VMSID:%s UUID:%s 错误:%s%n",
config.getVmsId(), uuid, e.getMessage());
}
}
}
// WebSocket连接管理器
class WebSocketConnectorLocal {
private static final int MAX_TEXT_BUFFER = 524288;
private static final int MAX_BINARY_BUFFER = 524288;
public static void connectAndSubscribe(VMSConfigLocal config, int requestCount) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.setDefaultMaxTextMessageBufferSize(MAX_TEXT_BUFFER);
container.setDefaultMaxBinaryMessageBufferSize(MAX_BINARY_BUFFER);
List<Transport> transports = Arrays.asList(
new WebSocketTransport(new StandardWebSocketClient(container)),
new RestTemplateXhrTransport()
);
SockJsClient webSocketClient = new SockJsClient(transports);
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
// 配置消息转换器
List<MessageConverter> converters = Arrays.asList(
new MappingJackson2MessageConverter(),
new StringMessageConverter(),
new ByteArrayMessageConverter()
);
stompClient.setMessageConverter(new CompositeMessageConverter(converters));
// 构建连接URL
String url = String.format("wss://127.0.0.1:8081/api/vms/%s/ws/status", config.getVmsId());
// 设置HTTP头
WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
headers.set("Cookie", config.getCookie());
headers.set("CSRF-TOKEN", config.getCsrfToken());
headers.set("SourceType", "vms_web");
headers.set("Content-Type", "application/json;charset=UTF-8");
// 设置STOMP头
StompHeaders connectHeaders = new StompHeaders();
connectHeaders.set("CSRF-TOKEN", config.getCsrfToken());
connectHeaders.set("accept-version", "1.1,1.0");
// 创建连接
stompClient.connect(url, headers, connectHeaders, new StompSessionHandlerAdapter() {
@Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
System.out.println("************ WebSocket连接成功 VMSID:" + config.getVmsId() + "*****\n");
ExecutorService requestExecutor = Executors.newFixedThreadPool(requestCount);
for (int i = 0; i < requestCount; i++) {
String uuid = "disorder-" + UUID.randomUUID().toString().substring(0, 8);
// 构建真实订阅路径
String realTopic = String.format(
"/topic/vms/%s/users/%s/device-add-progress/%s/v2",
config.getVmsId(),
config.getUserId(),
uuid
);
// 订阅消息
// String subscribeUrl = "/topic/vms/{vmsId}/users/{userId}/device-add-progress/{uuid}/v2";
session.subscribe(realTopic, new StompFrameHandler() {
@Override
public Type getPayloadType(StompHeaders headers) {
if (headers.getContentType() != null &&
headers.getContentType().toString().contains("application/json")) {
return Map.class;
}
return byte[].class;
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {
if (payload instanceof byte[]) {
String jsonStr = new String((byte[]) payload, StandardCharsets.UTF_8);
System.out.println("原始JSON消息: " + jsonStr);
}
// 处理自动转换的JSON对象
else if (payload instanceof Map) {
Map<?, ?> jsonMap = (Map<?, ?>) payload;
System.out.println("解析后的JSON消息: " + jsonMap);
}
// 处理字符串
else if (payload instanceof String) {
System.out.println("文本消息: " + payload);
}
}
});
// 延迟后发送请求(确保订阅生效)
final String finalUuid = uuid;
requestExecutor.execute(() -> {
try {
Thread.sleep(100); // 100ms延迟确保订阅注册完成
HttpUtilForLocal.sendDemoRequest(config, finalUuid);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
requestExecutor.shutdown();
}
});
} catch (Exception e) {
System.err.println("WebSocket连接异常 VMSID:" + config.getVmsId());
e.printStackTrace();
}
}
}
@SpringBootApplication
public class WebsocketClientApplication {
private static final int PARALLEL_REQUESTS_PER_VMS = 1; // 每个vmsId的请求数
public static void main(String[] args) throws InterruptedException {
SSLUtil.disableSSLVerification();
// 1. 准备测试用的vmsId配置列表
List<VMSConfigLocal> vmsConfigs = Arrays.asList(
// 主测试vmsId
new VMSConfigLocal(
"demo_vms",
"JSESSIONID=C2E3CC056311006FCC75B6398001B4ED; VMS_SID=v-ca119f6c-e0b2-4d55-91d8-8cd5c0091c8b",
"fec60c7ab64e4e2ba1ddcda283f8e372",
"68c269674804b811bea339e3"
)
// 额外测试vmsId(示例)
// new VMSConfig(
// "a84976add940407cbd7a76d66f5e8602",
// "VMS_SID=a1-56563ebd-d42c-4f86-a924-94a175659e5b; JSESSIONID=BF89313521CE8BEC0ACCF2141CC5913D",
// "809124f81f5d439db4de216f9831cc8c",
// "userId101"
// ),
// new VMSConfig(
// "a84976add940407cbd7a76d66f5e8603",
// "VMS_SID=a1-56563ebd-d42c-4f86-a924-94a175659e5b; JSESSIONID=BF89313521CE8BEC0ACCF2141CC5913D",
// "809124f81f5d439db4de216f9831cc8c",
// "userId102"
// )
);
// 2. 为每个vmsId创建WebSocket连接和订阅,订阅之后并行发送HTTP请求
vmsConfigs.forEach(config -> WebSocketConnectorLocal.connectAndSubscribe(config, PARALLEL_REQUESTS_PER_VMS));
System.out.println("客户端已启动,等待消息...");
Thread.sleep(Long.MAX_VALUE);
}
}
这是客户端的代码,怎么修改
最新发布