蒙国造博客

实现 Java WebSocket 客户端的 3 种方法

本文整理了3种 Java 语言实现 WebSocket 客户端的方式,分别是使用Javax.websocket-apiJava-WebSocketJakarta WebSocket,三种方法达到的效果一致,都能与 Websocket 服务端完成良好通信。

注意:要进行 WebSocket 通信,必须首先启动 WebSocket 服务端,再启动客户端。

方法1:使用 javax.websocket-api

javax.websocket-api提供了 websocket 通信的规范,定义了一些抽象类,只有部分实现,另外一部分需要手动实现或者通过引入其他依赖来解决,比如javax.websocket.ContainerProvider,所以不能直接用。 否则会报错:

java.lang.RuntimeException: Could not find an implementation class.

    at javax.websocket.ContainerProvider.getWebSocketContainer(ContainerProvider.java:73)
    at com.awaimai.Client1.<init>(Client1.java:13)
    at com.awaimai.Client1Test.sendMessage(Client1Test.java:11)
    ...
  1. 下面的 3 个依赖都包含了实现javax.websocket-api抽象方法的类,引入其中一个就能使用 Websocket 客户端了。
<!-- 具体实现类是:org.eclipse.jetty.websocket.jsr356.JettyClientContainerProvider -->
<dependency>
    <groupId>org.eclipse.jetty.websocket</groupId>
    <artifactId>javax-websocket-client-impl</artifactId>
    <version>9.4.43.v20210629</version>
</dependency>

<!-- 具体实现类是:org.apache.tomcat.websocket.WsContainerProvider -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    <version>2.5.4</version>
</dependency>

<!-- 具体实现类是:org.apache.tomcat.websocket.WsContainerProvider,其实上面的 spring-boot-starter-websocket 用的就是这个依赖,所以我们其实可以直接用,避免引入其他不必要的组建。但是!!注意!!,这个依赖10.xx版本之后已经不包含 Websocket 组件,最多可用到 9.0.52 版本 -->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-websocket</artifactId>
    <version>9.0.52</version>
</dependency>
  1. 定义客户端代码如下,我们命名它为Client1。该类的作用是:当它从Websocket server接收到信息时,在终端中直接打印出来,如果调用sendMessage()方法,则会向 server 发送指定的文本消息
package com.awaimai;

import javax.websocket.*;
import java.io.IOException;
import java.net.URI;

@ClientEndpoint
public class Client1 {
    private Session session;

    public Client1(URI uri) {
        try {
            session = ContainerProvider.getWebSocketContainer().connectToServer(this, uri);
        } catch (DeploymentException | IOException e) {
            e.printStackTrace();
        }
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println(message);
    }

    public void sendMessage(String str) {
        session.getAsyncRemote().sendText(str);
    }
}
  1. 测试类,测试类的作用是:每隔1秒钟向服务端发送内容为Hello的消息。
package com.awaimai;

import org.junit.jupiter.api.Test;
import java.net.URI;
import java.net.URISyntaxException;

class Client1Test {
    @Test
    void sendMessage() throws InterruptedException, URISyntaxException {
        Client1 client1 = new Client1(new URI("ws://localhost:9999"));

        while (true) {
            client1.sendMessage("Hello");
            Thread.sleep(1000);
        }
    }
}

先打开 Websocket 服务端,再运行测试类,就能看到终端不断的收到自己发到服务器,再从服务端反回来的消息了(当然,这里需要服务端要广播自己收到的信息)。

方法2:使用 Java-WebSocket

Java-WebSocket 是一套完整的实现了 Websocket Client 和 Server 的组件,使用使用也比较简单。

  1. 首先,添加依赖:
<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.5.2</version>
</dependency>
  1. 编写实现类Client2
package com.awaimai;

import java.net.URI;
import java.net.URISyntaxException;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

public class Client2 extends WebSocketClient {
    public Client2(URI serverURI) {
        super(serverURI);
    }

    @Override
    public void onOpen(ServerHandshake serverHandshake) {
    }

    @Override
    public void onMessage(String message) {
        System.out.println(message);
    }

    @Override
    public void onClose(int i, String s, boolean b) {
    }

    @Override
    public void onError(Exception e) {
    }
}
  1. 测试类,功能和上面的例子一样,每隔1秒钟向服务端发送一条消息。
package com.awaimai;

import org.junit.jupiter.api.Test;
import java.net.URI;
import java.net.URISyntaxException;

class Client2Test {
    @Test
    void sendMessage() throws InterruptedException, URISyntaxException {
        Client2 client2 = new Client2(new URI("ws://localhost:9999"));
        client2.connect();

        while (true) {
            if (client2.isOpen()) {
                client2.send("Hello");
            }
            Thread.sleep(1000);
        }
    }
}

测试方法同上,先打开 Websocket 服务端,再运行测试方法。

方法3: 使用 Jakarta WebSocket

Jakarta WebSocket 用法和 javax.websocket-api 几乎是一样的,它有一套自己的接口,还有一套实现。

  1. 引入接口依赖和实现依赖:
<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-api</artifactId>
    <version>9.1.0</version>
</dependency>

<dependency>
    <groupId>org.glassfish.tyrus.bundles</groupId>
    <artifactId>tyrus-standalone-client</artifactId>
    <version>2.0.0</version>
</dependency>
  1. 编写实现类 Client3,可以对比一下 javax.websocket-api,使用方式是一样的:
package com.awaimai;

import jakarta.websocket.*;
import java.io.IOException;
import java.net.URI;

@ClientEndpoint
public class Client3 {
    Session session = null;

    public Client3(URI endpointURI) {
        try {
            session = ContainerProvider.getWebSocketContainer().connectToServer(this, endpointURI);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println(message);
    }

    public void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  1. 测试类
package com.awaimai;

import org.junit.jupiter.api.Test;
import java.net.URI;
import java.net.URISyntaxException;

class Client3Test {

    @Test
    void sendMessage() throws URISyntaxException, InterruptedException {
        Client3 client3 = new Client3(new URI("ws://localhost:9999"));

        while (true) {
            client3.sendMessage("Hello");
            Thread.sleep(1000);
        }
    }
}

运行后,效果跟上面是一样的。

退出移动版