WebSocket 通过提供双向、全双工、实时的客户端/服务器通信,为服务器和网络浏览器之间的高效通信限制提供了一种替代方案。服务器可随时向客户端发送数据。由于它通过 TCP 运行,因此还能提供低延迟、低级别通信,并减少每条信息的开销。
在本教程中,我们将通过创建一个类似聊天的应用程序来探索 WebSockets 的 Java API。
JSR 356
JSR 356(即 Java API for WebSocket)指定了一个 API,Java 开发人员可以使用该 API 将 WebSocket 集成到其应用程序中(无论是在服务器端还是在 Java 客户端)。
该 Java API 提供服务器端和客户端组件:
- Server:javax.websocket.server包中的所有内容
- Client : javax.websocket包的内容,由客户端API以及服务器和客户端的公共库组成
使用 WebSocket 构建聊天
让我们构建一个非常简单的类似聊天的应用程序。任何用户都可以从任何浏览器打开聊天室,输入姓名,登录聊天室,然后开始与连接到聊天室的每个人进行交流。
首先将最新的依赖项添加到pom.xml文件中:
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
为了将 Java 对象转换为 JSON 表示形式,反之亦然,这里将使用 Gson:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>
端点配置
有两种配置端点的方法:基于注解和基于扩展。我们既可以扩展 javax.websocket.Endpoint 类,也可以使用专用的方法级注解。与编程模式相比,注解模式能带来更简洁的代码,因此注解已成为编码的传统选择。我们将使用以下注解来处理 WebSocket 端点生命周期事件:
- @ServerEndpoint:如果使用 @ServerEndpoint 进行装饰,容器就会确保该类作为 WebSocket 服务器监听特定 URI 空间的可用性。
- @ClientEndpoint: 使用此注解装饰的类将被视为 WebSocket 客户端。
- @OnOpen: 当一个新的 WebSocket 连接启动时,容器会调用带有 @OnOpen 的 Java 方法。
- @OnMessage: 当有信息发送到端点时,带有 @OnMessage 注解的 Java 方法会从 WebSocket 容器接收信息。
- @OnError: 当通信出现问题时,会调用带有 @OnError 的方法。
- @OnClose: 用于装饰容器在 WebSocket 连接关闭时调用的 Java 方法
编写服务器端点
我们将用 @ServerEndpoint 注释来声明一个 Java 类 WebSocket 服务器端点。我们还将指定部署端点的 URI。我们定义的 URI 相对于服务器容器的根目录,并且必须以斜线开头:
@ServerEndpoint(value = "/chat/{username}")
public class ChatEndpoint {
@OnOpen
public void onOpen(Session session) throws IOException {
// Get session and WebSocket connection
}
@OnMessage
public void onMessage(Session session, Message message) throws IOException {
// Handle new messages
}
@OnClose
public void onClose(Session session) throws IOException {
// WebSocket connection closes
}
@OnError
public void onError(Session session, Throwable throwable) {
// Do error handling here
}
}
上面的代码是类似聊天的应用程序的服务器端点框架。正如所看到的,我们有四个注解映射到它们各自的方法。下面,可以看到这些方法的实现:
@ServerEndpoint(value="/chat/{username}")
public class ChatEndpoint {
private Session session;
private static Set<ChatEndpoint> chatEndpoints
= new CopyOnWriteArraySet<>();
private static HashMap<String, String> users = new HashMap<>();
@OnOpen
public void onOpen(
Session session,
@PathParam("username") String username) throws IOException {
this.session = session;
chatEndpoints.add(this);
users.put(session.getId(), username);
Message message = new Message();
message.setFrom(username);
message.setContent("Connected!");
broadcast(message);
}
@OnMessage
public void onMessage(Session session, Message message)
throws IOException {
message.setFrom(users.get(session.getId()));
broadcast(message);
}
@OnClose
public void onClose(Session session) throws IOException {
chatEndpoints.remove(this);
Message message = new Message();
message.setFrom(users.get(session.getId()));
message.setContent("Disconnected!");
broadcast(message);
}
@OnError
public void onError(Session session, Throwable throwable) {
// Do error handling here
}
private static void broadcast(Message message)
throws IOException, EncodeException {
chatEndpoints.forEach(endpoint -> {
synchronized (endpoint) {
try {
endpoint.session.getBasicRemote().
sendObject(message);
} catch (IOException | EncodeException e) {
e.printStackTrace();
}
}
});
}
}
当有新用户登录时,(@OnOpen) 会立即映射到活跃用户的数据结构中。然后创建一条信息,并使用广播方法发送到所有端点。
每当连接的任何用户发送新消息(@OnMessage)时,也会使用该方法;这是聊天的主要目的。
如果出现错误,带有注解 @OnError 的方法会进行处理。我们可以使用该方法记录错误信息,并清除端点。
最后,当用户不再连接聊天时,方法 @OnClose 会清除端点,并向所有用户广播用户已断开连接。
消息类型
WebSocket 规范支持两种线上数据格式:Text 和 Binary。API 支持这两种格式,添加了使用 Java 对象的功能以及运行状况检查消息 (ping-pong),如规范中所定义:
- Text: 任何文本数据(java.lang.String、基元或等价的封装类)
- Binary: 二进制数据(如音频、图像等),由 java.nio.ByteBuffer 或 byte[] (字节数组)表示
- Java objects: 通过该应用程序接口,我们可以在代码中使用本地(Java objects)表示形式,并使用自定义转换器(编码器/解码器)将其转换为 WebSocket 协议允许的兼容在线格式(Text 和 Binary)。
- Ping-Pong:javax.websocket.PongMessage 是 WebSocket 对等方为响应健康检查(Ping)请求而发送的确认信息。
对于我们的应用程序,我们将使用 Java Objects。我们将创建用于编码和解码消息的类。
编码器
编码器采用 Java 对象并生成适合作为消息传输的典型表示,例如 JSON、XML 或二进制表示。可以通过实现Encoder.Text<T>或Encoder.Binary<T>接口来使用编码器。
在下面的代码中,我们将定义要编码的Message类,并在编码方法中,我们将使用 Gson 将 Java 对象编码为 JSON:
public class Message {
private String from;
private String to;
private String content;
//standard constructors, getters, setters
}
public class MessageEncoder implements Encoder.Text<Message> {
private static Gson gson = new Gson();
@Override
public String encode(Message message) throws EncodeException {
return gson.toJson(message);
}
@Override
public void init(EndpointConfig endpointConfig) {
// Custom initialization logic
}
@Override
public void destroy() {
// Close resources
}
}
解码器
解码器与编码器相反。我们用它来将数据转换回 Java 对象。解码器可以使用Decoder.Text<T>或Decoder.Binary<T>接口来实现。
正如我们在编码器中看到的,decode方法是我们获取发送到端点的消息中检索到的 JSON 的地方,并使用 Gson 将其转换为名为Message 的 Java 类:
public class MessageDecoder implements Decoder.Text<Message> {
private static Gson gson = new Gson();
@Override
public Message decode(String s) throws DecodeException {
return gson.fromJson(s, Message.class);
}
@Override
public boolean willDecode(String s) {
return (s != null);
}
@Override
public void init(EndpointConfig endpointConfig) {
// Custom initialization logic
}
@Override
public void destroy() {
// Close resources
}
}
在服务器端点设置编码器和解码器
让我们在类级注解 @ServerEndpoint 中添加为数据编码和解码创建的类,从而将一切整合在一起:
@ServerEndpoint(
value="/chat/{username}",
decoders = MessageDecoder.class,
encoders = MessageEncoder.class )
每次消息发送到端点时,它们都会自动转换为 JSON 或 Java 对象。
结论
在本文中,我们分析了 WebSockets 的 Java API,并了解了它如何帮助我们构建应用程序,比如这个实时聊天程序。
我们讨论了创建端点的两种编程模型:注解和编程。然后,我们使用注解模型为我们的应用程序定义了一个端点以及生命周期方法。
此外,为了能够在服务器和客户端之间来回通信,我们演示了我们需要编码器和解码器来将 Java 对象转换为 JSON,反之亦然。
JSR 356 API 非常简单,基于注解的编程模型使构建 WebSocket 应用程序变得非常容易。
要运行示例中构建的应用程序,我们只需将 war 文件部署到 Web 服务器中,然后访问 URL:http://localhost:8080/java-websocket/。在这里可以找到资源库的链接。
作者:baeldung
原文:https://www.baeldung.com/java-websockets
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/im/37819.html