Java WebSocket客户端--java.net.http.HttpClient

  1. 在同步模式下请求 WebSocket 服务端
java 复制代码
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class WebSocketSyncClientExample {

    private static final String WS_SERVER_URI = "ws://echo.websocket.events"; // 替换为你的WebSocket服务端地址
    private static CountDownLatch latch; // 用于同步等待事件发生

    public static void main(String[] args) {
        System.out.println("尝试连接 WebSocket 服务端: " + WS_SERVER_URI);

        latch = new CountDownLatch(1); // 初始化计数器,等待连接建立

        HttpClient httpClient = HttpClient.newHttpClient();

        try {
            // 构建WebSocket连接并传入自定义的监听器
            // buildAsync 返回一个 CompletionStage,表示异步连接过程
            CompletionStage<WebSocket> wsFuture = httpClient.newWebSocketBuilder()
                    .buildAsync(URI.create(WS_SERVER_URI), new CustomWebSocketListener());

            // 阻塞主线程,直到WebSocket连接成功建立(或超时)
            WebSocket webSocket = wsFuture.get(10, TimeUnit.SECONDS); // 最多等待10秒

            System.out.println("WebSocket 连接已建立。");

            // 发送一条消息
            String messageToSend = "Hello WebSocket Server!";
            System.out.println("发送消息: " + messageToSend);
            webSocket.sendText(messageToSend, true); // true表示这是最后一部分消息

            // 再次初始化latch,等待接收消息或连接关闭
            latch = new CountDownLatch(1);
            latch.await(15, TimeUnit.SECONDS); // 等待接收消息或超时

            // 关闭连接
            System.out.println("准备关闭 WebSocket 连接...");
            webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "客户端主动关闭").join(); // join() 阻塞直到关闭完成
            System.out.println("WebSocket 连接已关闭。");

        } catch (Exception e) {
            System.err.println("WebSocket 连接或通信过程中发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }

    // 自定义WebSocket监听器
    private static class CustomWebSocketListener implements WebSocket.Listener {

        @Override
        public void onOpen(WebSocket webSocket) {
            System.out.println("WebSocket: 连接已打开。");
            // 当连接打开时,减少latch计数,解除主线程的阻塞
            latch.countDown();
            // 可以在这里发送初始消息
            // webSocket.sendText("Client connected!", true);
        }

        @Override
        public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
            System.out.println("WebSocket: 收到文本消息: " + data);
            // 收到消息后,减少latch计数,解除主线程的阻塞
            latch.countDown();
            return WebSocket.Listener.super.onText(webSocket, data, last);
        }

        @Override
        public CompletionStage<?> onBinary(WebSocket webSocket, java.nio.ByteBuffer data, boolean last) {
            System.out.println("WebSocket: 收到二进制消息 (长度: " + data.remaining() + ")");
            latch.countDown();
            return WebSocket.Listener.super.onBinary(webSocket, data, last);
        }

        @Override
        public CompletionStage<?> onPing(WebSocket webSocket, java.nio.ByteBuffer message) {
            System.out.println("WebSocket: 收到 Ping 帧。");
            return WebSocket.Listener.super.onPing(webSocket, message);
        }

        @Override
        public CompletionStage<?> onPong(WebSocket webSocket, java.nio.ByteBuffer message) {
            System.out.println("WebSocket: 收到 Pong 帧。");
            return WebSocket.Listener.super.onPong(webSocket, message);
        }

        @Override
        public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
            System.out.println("WebSocket: 连接已关闭。状态码: " + statusCode + ", 原因: " + reason);
            // 连接关闭时,减少latch计数,解除主线程的阻塞
            latch.countDown();
            return WebSocket.Listener.super.onClose(webSocket, statusCode, reason);
        }

        @Override
        public void onError(WebSocket webSocket, Throwable error) {
            System.err.println("WebSocket: 发生错误: " + error.getMessage());
            error.printStackTrace();
            // 发生错误时,减少latch计数,解除主线程的阻塞
            latch.countDown();
        }
    }
}
  1. 在这个异步版本中,主线程不会阻塞等待连接建立或消息收发。所有的操作都通过 CompletionStageWebSocket.Listener 回调来处理。为了确保主线程不会在异步操作完成前退出,我们仍然会使用一个 CountDownLatch 来等待整个通信过程结束。
java 复制代码
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class WebSocketAsyncClientExample {

    private static final String WS_SERVER_URI = "ws://echo.websocket.events"; // 替换为你的WebSocket服务端地址
    private static final CountDownLatch appShutdownLatch = new CountDownLatch(1); // 用于控制主线程何时退出

    public static void main(String[] args) {
        System.out.println("尝试异步连接 WebSocket 服务端: " + WS_SERVER_URI);

        HttpClient httpClient = HttpClient.newHttpClient();

        // 异步构建WebSocket连接
        CompletableFuture<WebSocket> webSocketFuture = httpClient.newWebSocketBuilder()
                .buildAsync(URI.create(WS_SERVER_URI), new CustomAsyncWebSocketListener());

        // 使用thenAccept处理连接成功后的逻辑
        webSocketFuture.thenAccept(webSocket -> {
            System.out.println("WebSocket 连接已成功建立。");
            // 连接成功后,异步发送一条消息
            String messageToSend = "Hello from Async Client!";
            System.out.println("异步发送消息: " + messageToSend);
            webSocket.sendText(messageToSend, true)
                     .thenRun(() -> System.out.println("消息发送完成。"))
                     .exceptionally(ex -> {
                         System.err.println("消息发送失败: " + ex.getMessage());
                         return null;
                     });

            // 可以在这里设置一个定时器,在一段时间后关闭连接
            // 或者等待接收到特定消息后关闭
            // 为了示例,我们让它运行一段时间,然后通过监听器的onClose或onError触发appShutdownLatch
        }).exceptionally(ex -> {
            System.err.println("WebSocket 连接失败: " + ex.getMessage());
            appShutdownLatch.countDown(); // 连接失败,立即释放主线程
            return null;
        });

        System.out.println("主线程继续执行,等待WebSocket通信完成...");

        try {
            // 阻塞主线程,直到appShutdownLatch计数为0(即WebSocket通信结束或出错)
            appShutdownLatch.await(30, TimeUnit.SECONDS); // 最多等待30秒
            System.out.println("主线程检测到WebSocket通信结束或超时,程序退出。");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("主线程等待被中断: " + e.getMessage());
        }
    }

    // 自定义异步WebSocket监听器
    private static class CustomAsyncWebSocketListener implements WebSocket.Listener {

        private WebSocket currentWebSocket; // 保存WebSocket实例,以便在其他方法中使用

        @Override
        public void onOpen(WebSocket webSocket) {
            this.currentWebSocket = webSocket;
            System.out.println("Listener: WebSocket 连接已打开。");
            // 在这里可以发送欢迎消息等
        }

        @Override
        public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
            System.out.println("Listener: 收到文本消息: " + data);
            // 收到消息后,可以根据业务逻辑决定是否关闭连接
            // 例如,如果收到特定消息,就关闭
            if (data.toString().contains("close")) {
                System.out.println("Listener: 收到 'close' 消息,准备关闭连接。");
                return webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "收到关闭指令")
                                .thenRun(() -> System.out.println("Listener: 连接关闭指令已发送。"));
            }
            return WebSocket.Listener.super.onText(webSocket, data, last);
        }

        @Override
        public CompletionStage<?> onBinary(WebSocket webSocket, java.nio.ByteBuffer data, boolean last) {
            System.out.println("Listener: 收到二进制消息 (长度: " + data.remaining() + ")");
            return WebSocket.Listener.super.onBinary(webSocket, data, last);
        }

        @Override
        public CompletionStage<?> onPing(WebSocket webSocket, java.nio.ByteBuffer message) {
            System.out.println("Listener: 收到 Ping 帧。");
            return WebSocket.Listener.super.onPing(webSocket, message);
        }

        @Override
        public CompletionStage<?> onPong(WebSocket webSocket, java.nio.ByteBuffer message) {
            System.out.println("Listener: 收到 Pong 帧。");
            return WebSocket.Listener.super.onPong(webSocket, message);
        }

        @Override
        public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
            System.out.println("Listener: WebSocket 连接已关闭。状态码: " + statusCode + ", 原因: " + reason);
            appShutdownLatch.countDown(); // 连接关闭,释放主线程
            return WebSocket.Listener.super.onClose(webSocket, statusCode, reason);
        }

        @Override
        public void onError(WebSocket webSocket, Throwable error) {
            System.err.println("Listener: WebSocket 发生错误: " + error.getMessage());
            error.printStackTrace();
            appShutdownLatch.countDown(); // 发生错误,释放主线程
        }
    }
}
相关推荐
程序猿大帅4 小时前
别再只当调包侠了:用 Spring AI 落地 Function Calling,我被大模型硬生生砸出了三个大坑
java
程序员晓琪5 小时前
约定大于配置:基于 Java 包名自动生成 API 版本路由的最佳实践
java·spring boot·后端
Flittly5 小时前
【AgentScope Java新手村系列】(11)中断与恢复
java·spring boot·spring
众少成多积小致巨6 小时前
JNI (Java Native Interface) 技术手册中文参考指南
android·java·c++
东坡白菜6 小时前
破局全栈:前端开发的Java入门实战记录—JPA(2)
java·后端
小码编匠6 小时前
C# 工控上位机必备:数据转换工具类与十个核心模块
后端·c#·.net
SimonKing12 小时前
艹,维护AI写的代码,我心态崩了......
java·后端·程序员
用户2986985301413 小时前
Java Word 文档样式进阶:段落与文本背景色设置完全指南
java·后端
小bo波1 天前
从"任意文件复制"深挖Java I/O:字符流与字节流的本质抉择
java·nio·io流·后端开发·文件复制