java WebSocketClient 作为客户端订阅处理消息

本文介绍了一个基于Java的WebSocket客户端实现,包括连接建立、消息接收、连接关闭及错误处理等核心功能。通过继承org.java_websocket.client.WebSocketClient类并重写其方法,实现了与服务器的稳定通信。此外,文章还提供了断线重连的相关资源链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.fh.controller.websocket;

import com.fh.service.uav.UAVManageService;
import com.fh.util.Logger;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;
import java.net.URISyntaxException;
 

public class MyWebSocketClient extends WebSocketClient {

    
   
    public MyWebSocketClient(String url) throws URISyntaxException {
        super(new URI(url));
      
    }

    @Override
    public void onOpen(ServerHandshake shake) {
        logger.info("握手,打开链接");
    }

    @Override
    public void onMessage(String paramString) {
       //消息处理
    }

    @Override
    public void onClose(int paramInt, String paramString, boolean paramBoolean) {
        logger.info("websocket 断开:"  + paramInt + "  " + paramString + "    " +paramBoolean);
        logger.info("关闭连接");
    }

    @Override
    public void onError(Exception e) {
        logger.info("出现异常:" + e.toString());
    }

}

 

断线重连见:https://blog.csdn.net/a81150605/article/details/105813096

<think>我们正在实现一个WebSocket客户端,用于订阅服务端的服务。根据引用[1]和引用[2],我们知道WebSocket可以实现服务端向客户端消息推送(即订阅模式)。在Java中,我们可以使用JSR-356标准(Java API for WebSocket)来实现WebSocket客户端。 步骤: 1. 添加依赖:在Spring Boot项目中,我们需要添加`spring-boot-starter-websocket`依赖。 2. 创建WebSocket客户端类:使用`@Component`注解,并实现连接、发送消息、接收消息的方法。 3. 连接服务端:通过`WebSocketContainer`连接到指定的服务端URI。 4. 处理消息:通过实现`MessageHandler`接口来处理从服务端接收到的消息。 5. 启动时连接:使用`CommandLineRunner`在应用启动时自动连接WebSocket服务器。 6. 处理重连:在连接关闭时,实现重连机制。 注意:在订阅模式中,客户端在连接建立后,通常需要发送一个订阅请求(例如:指定订阅的主题),然后服务端会根据订阅的主题推送消息。 下面是一个详细的实现: 第一步:添加Maven依赖(如果使用Maven) ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 第二步:编写WebSocket客户端类 ```java import org.springframework.stereotype.Component; import javax.websocket.*; import java.net.URI; @Component public class WebSocketClient { private Session session; // WebSocket会话 // 连接服务端 public void connect(String serverUri) { try { WebSocketContainer container = ContainerProvider.getWebSocketContainer(); container.connectToServer(new Endpoint() { @Override public void onOpen(Session session, EndpointConfig config) { System.out.println("连接成功"); WebSocketClient.this.session = session; // 添加消息处理器,处理文本消息 session.addMessageHandler(new MessageHandler.Whole<String>() { @Override public void onMessage(String message) { handleServerMessage(message); } }); // 连接成功后,发送订阅请求(例如:订阅主题) try { subscribe("your_topic"); } catch (Exception e) { e.printStackTrace(); } } @Override public void onClose(Session session, CloseReason reason) { System.out.println("连接关闭: " + reason); // 尝试重连 reconnect(serverUri); } @Override public void onError(Session session, Throwable throwable) { System.out.println("连接错误"); throwable.printStackTrace(); } }, ClientEndpointConfig.Builder.create().build(), URI.create(serverUri)); } catch (Exception e) { e.printStackTrace(); } } // 发送订阅请求 public void subscribe(String topic) throws Exception { if (session != null && session.isOpen()) { // 假设订阅消息的格式为:{"action":"subscribe","topic":"xxx"} String subscribeMessage = String.format("{\"action\":\"subscribe\",\"topic\":\"%s\"}", topic); session.getBasicRemote().sendText(subscribeMessage); } else { throw new IllegalStateException("WebSocket连接未就绪"); } } // 处理收到的消息(服务端推送的订阅消息) private void handleServerMessage(String message) { System.out.println("收到服务端消息: " + message); // 这里可以解析消息,并根据业务逻辑处理 } // 发送消息到服务端(如果需要) public void sendMessage(String message) throws Exception { if (session != null && session.isOpen()) { session.getBasicRemote().sendText(message); } else { throw new IllegalStateException("WebSocket连接未就绪"); } } // 自动重连机制 private void reconnect(String serverUri) { try { Thread.sleep(5000); // 等待5秒后重连 System.out.println("尝试重新连接..."); connect(serverUri); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } ``` 第三步:在应用启动时连接WebSocket服务器 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class WebSocketStarter implements CommandLineRunner { private final WebSocketClient webSocketClient; @Autowired public WebSocketStarter(WebSocketClient webSocketClient) { this.webSocketClient = webSocketClient; } @Override public void run(String... args) { // 启动时连接WebSocket服务器,并指定服务端地址 webSocketClient.connect("ws://your-server-address:port/ws-endpoint"); } } ``` 第四步:解决服务注入问题(如果需要在WebSocketClient中使用其他Spring管理的Bean) 由于WebSocketClient是由JSR-356的API创建的,不是由Spring直接管理,因此我们使用一个静态成员和setter注入的方式。 ```java @Component public class WebSocketClient { // 用于在非Spring管理的对象中注入服务 private static MessageService messageService; @Autowired public void setMessageService(MessageService messageService) { WebSocketClient.messageService = messageService; } // 在handleServerMessage方法中使用 private void handleServerMessage(String message) { // 使用注入的服务处理消息 messageService.process(message); } // ... 其他代码 ... } ``` 第五步:配置类(可选,如果自动装配不生效) ```java @Configuration public class WebSocketClientConfig { @Bean public WebSocketClient webSocketClient() { return new WebSocketClient(); } @Bean public WebSocketStarter webSocketStarter() { return new WebSocketStarter(webSocketClient()); } } ``` 使用示例:在需要的地方发送消息(非订阅消息) ```java @Service public class SomeService { @Autowired private WebSocketClient webSocketClient; public void doSomething() { try { webSocketClient.sendMessage("Hello"); } catch (Exception e) { e.printStackTrace(); } } } ``` 说明: 1. 在连接建立后,我们立即发送了一个订阅请求(subscribe方法),订阅了指定的主题。这样服务端就会将相关主题的消息推送给客户端。 2. 当连接关闭时,我们尝试重新连接(reconnect方法)。 3. 在收到消息时,我们通过handleServerMessage方法处理,这里可以调用业务服务来处理消息。 注意:在实际应用中,订阅消息格式和服务端的要求有关,请根据实际情况调整订阅消息的格式。 引用[4]中提到,在云容器引擎上部署的服务,客户端在第一次订阅时会开放对服务端的通信权限,但10分钟内没有数据通信就会断开。因此,我们还需要考虑心跳机制来维持连接。可以在连接建立后,定时发送心跳消息(例如:每5分钟发送一次心跳)。 添加心跳机制: 我们可以在连接成功后启动一个定时任务,定时发送心跳消息。 在WebSocketClient类中添加: ```java private ScheduledExecutorService scheduler; // 在onOpen方法中启动心跳 @Override public void onOpen(Session session, EndpointConfig config) { // ... 其他代码 ... startHeartbeat(); } // 启动心跳 private void startHeartbeat() { scheduler = Executors.newSingleThreadScheduledExecutor(); // 每5分钟发送一次心跳 scheduler.scheduleAtFixedRate(() -> { try { if (session != null && session.isOpen()) { session.getBasicRemote().sendText("{\"action\":\"heartbeat\"}"); } } catch (Exception e) { e.printStackTrace(); } }, 0, 5, TimeUnit.MINUTES); // 初始延迟0,之后每5分钟执行一次 } // 在onClose方法中停止心跳 @Override public void onClose(Session session, CloseReason reason) { // ... 其他代码 ... if (scheduler != null) { scheduler.shutdown(); } } ``` 这样,每隔5分钟发送一次心跳,可以避免连接因为长时间没有数据而被关闭。 总结:我们实现了一个具有订阅功能、自动重连和心跳机制的WebSocket客户端,可以用于订阅服务端的服务。 相关问题: 1. 如何实现WebSocket客户端的负载均衡? 2. WebSocket客户端如何实现消息确认机制? 3. Spring Boot WebSocket客户端如何支持SSL/TLS加密? 4. 如何监控WebSocket客户端的连接状态和性能?</think>### Java WebSocket 客户端实现订阅服务的完整指南 #### 一、核心实现步骤 1. **添加依赖** (pom.xml) ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. **创建WebSocket客户端** (订阅模式) ```java import org.springframework.stereotype.Component; import javax.websocket.*; import java.net.URI; @Component public class SubscriptionClient { private Session session; private final String TOPIC = "your-topic-name"; // 订阅主题 // 连接并订阅服务 public void connectAndSubscribe(String serverUri) { try { WebSocketContainer container = ContainerProvider.getWebSocketContainer(); container.connectToServer(new Endpoint() { @Override public void onOpen(Session session, EndpointConfig config) { SubscriptionClient.this.session = session; // 添加消息处理器 session.addMessageHandler(String.class, message -> { handleSubscriptionMessage(message); }); // 发送订阅请求[^2] sendSubscriptionRequest(TOPIC); } @Override public void onClose(Session session, CloseReason reason) { System.out.println("连接关闭: " + reason); reconnect(serverUri); } @Override public void onError(Session session, Throwable thr) { System.err.println("WebSocket错误: " + thr.getMessage()); } }, ClientEndpointConfig.Builder.create().build(), URI.create(serverUri)); } catch (Exception e) { e.printStackTrace(); } } // 发送订阅请求 private void sendSubscriptionRequest(String topic) { try { if (session != null && session.isOpen()) { // 构建订阅消息格式 (JSON示例) String subscribeMsg = String.format( "{\"action\":\"subscribe\", \"topic\":\"%s\"}", topic ); session.getBasicRemote().sendText(subscribeMsg); } } catch (Exception e) { e.printStackTrace(); } } // 处理订阅消息 private void handleSubscriptionMessage(String message) { System.out.println("收到订阅消息: " + message); // 添加业务处理逻辑 } // 自动重连机制 private void reconnect(String serverUri) { try { Thread.sleep(5000); System.out.println("尝试重新连接..."); connectAndSubscribe(serverUri); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } ``` 3. **启动时自动连接** (CommandLineRunner) ```java @Component public class SubscriptionStarter implements CommandLineRunner { private final SubscriptionClient subscriptionClient; public SubscriptionStarter(SubscriptionClient subscriptionClient) { this.subscriptionClient = subscriptionClient; } @Override public void run(String... args) { // 启动时连接并订阅 subscriptionClient.connectAndSubscribe( "ws://your-server-address:port/subscription-endpoint" ); } } ``` #### 二、关键机制说明 1. **订阅流程**: - 连接建立后立即发送订阅请求 (`sendSubscriptionRequest`) - 服务端识别订阅请求后开始推送相关消息[^2] - 通过 `handleSubscriptionMessage` 处理推送数据 2. **消息格式** (推荐JSON): ```json // 订阅请求 {"action": "subscribe", "topic": "stock-updates"} // 服务端推送 {"topic": "stock-updates", "data": {"symbol": "AAPL", "price": 182.3}} ``` 3. **连接管理**: - 自动重连机制确保订阅不中断 - 心跳机制维持连接活性(参考引用[4]) ```java // 在onOpen方法中添加 scheduler.scheduleAtFixedRate(() -> { if(session.isOpen()) { session.getAsyncRemote().sendText("{\"type\":\"heartbeat\"}"); } }, 0, 1, TimeUnit.MINUTES); ``` #### 三、高级配置 1. **SSL/TLS加密连接**: ```java // 使用wss协议 subscriptionClient.connectAndSubscribe( "wss://secure-server:443/subscription-path" ); // 信任所有证书(测试环境) System.setProperty("javax.net.ssl.trustStore", "NONE"); System.setProperty("javax.net.ssl.trustStoreType", "Windows-ROOT"); ``` 2. **消息确认机制**: ```java private void handleSubscriptionMessage(String message) { try { // 处理消息... // 发送确认回执 if(session.isOpen()) { session.getBasicRemote().sendText("{\"status\":\"ACK\"}"); } } catch (Exception e) { // 发送NACK session.getBasicRemote().sendText("{\"status\":\"NACK\"}"); } } ``` #### 四、测试用例 ```java @SpringBootTest public class SubscriptionTest { @Autowired private SubscriptionClient client; @Test public void testSubscription() throws InterruptedException { Thread.sleep(10000); // 等待订阅消息 // 验证是否收到预期消息 } } ``` ### 常见问题解决 1. **订阅失效问题** (引用[4]): - 添加定期心跳(每1-2分钟) - 服务端需配置合理的超时时间(>10分钟) - 在`onClose`中实现自动重新订阅 2. **服务注入问题**: ```java @Component public class SubscriptionClient { private static MessageProcessor processor; @Autowired public void setProcessor(MessageProcessor processor) { SubscriptionClient.processor = processor; } private void handleSubscriptionMessage(String message) { processor.process(message); // 使用注入的服务 } } ``` 3. **多主题订阅**: ```java public void subscribeMultiple(String... topics) { for (String topic : topics) { sendSubscriptionRequest(topic); } } ``` > **最佳实践**:订阅服务时,客户端和服务端应约定统一的消息协议格式,包含`action`、`topic`、`payload`等标准字段,确保通信的可靠性[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值