> 技术文档 > 如何使用Java WebSocket API实现客户端和服务器端的通信?_java websocket 服务端和客户端

如何使用Java WebSocket API实现客户端和服务器端的通信?_java websocket 服务端和客户端

WebSocket相关的方法主要涉及客户端和服务器端,以下是对其几个关键方法之间关联以及参数传递方式的介绍:

服务器端

Java中通常使用Jakarta WebSocket API(以前是Java EE的一部分,现在Jakarta EE 继续维护 )来实现WebSocket服务器端功能,常用注解有@ServerEndpoint, 关键方法包括:

  • onOpen(Session session):当客户端与服务器成功建立WebSocket连接时,该方法会被调用。
    • 方法关联:它是连接建立后的第一个触发方法,为后续的数据交互等操作做准备。通过它可以获取到代表该连接的 Session 对象,后续对连接的操作,如发送消息等,都依赖于这个 Session
    • 参数传递Session 参数由WebSocket容器在连接建立时自动创建并传递,它包含了与该连接相关的各种信息,如远程地址、请求头、发送消息的方法等。例如,你可以通过 session.getRemoteAddress() 获取客户端的远程地址。
  • onMessage(String message, Session session):当服务器接收到客户端发送的文本消息时会调用此方法(如果处理二进制消息,有对应的 onMessage(byte[] message, Session session) 方法 )。
    • 方法关联:它依赖于 onOpen 方法建立的连接,只有在连接成功建立后,才会接收并处理客户端发送的消息。在处理完消息后,也可以通过传入的 Session 对象向客户端发送响应消息。
    • 参数传递message 参数是客户端实际发送的文本内容,由WebSocket容器从网络传输数据中解析出来并传递。session 依然是代表该连接的会话对象,与 onOpen 方法中的 session 是同一个实例,用于在处理消息过程中操作连接,比如 session.getBasicRemote().sendText(\"Response message\"); 向客户端发送响应文本消息。
  • onClose(Session session, CloseReason closeReason):当WebSocket连接关闭时调用,无论是正常关闭还是异常关闭。
    • 方法关联:它是连接生命周期的最后阶段,在连接关闭时触发,用于进行一些资源清理、状态更新等操作。
    • 参数传递session 是即将关闭的连接会话对象。closeReason 由WebSocket容器根据连接关闭的情况生成并传递,包含了关闭的状态码和关闭原因描述,比如 closeReason.getCloseCode().getCode() 可以获取关闭状态码,closeReason.getReasonPhrase() 获取关闭原因描述。
  • onError(Session session, Throwable throwable):当WebSocket连接在处理过程中发生错误时调用。
    • 方法关联:它与其他方法在异常情况下产生关联,一旦在消息处理、连接建立等过程中出现异常,就会触发此方法。可以在该方法中记录错误日志,根据情况关闭连接等。
    • 参数传递session 是出现错误的连接会话对象。throwable 是具体的异常对象,包含了异常类型和堆栈信息等,通过 throwable.printStackTrace() 可以打印出异常堆栈信息以便排查问题。

客户端

使用 StandardWebSocketClient 等类来实现WebSocket客户端功能,关键方法有:

  • connectToServer(Endpoint endpoint, URI uri):用于发起WebSocket连接。
    • 方法关联:它是客户端建立连接的入口方法,后续的消息接收、发送等操作都依赖于成功建立的连接。
    • 参数传递endpoint 是一个实现了 Endpoint 接口的类实例,包含了处理连接的回调方法,如 onOpenonMessage 等。uri 是要连接的WebSocket服务器的地址,格式为 ws://wss:// 开头。例如:
WebSocketContainer container = ContainerProvider.getWebSocketContainer();MyEndpoint myEndpoint = new MyEndpoint();URI serverUri = new URI(\"ws://localhost:8080/your - websocket - path\");container.connectToServer(myEndpoint, serverUri);

其中 MyEndpoint 是自定义的实现了 Endpoint 接口的类。

  • sendText(String text)(在 Session 对象上调用 ):用于向服务器发送文本消息。
    • 方法关联:依赖于 connectToServer 方法成功建立的连接,只有在连接建立后才能发送消息。
    • 参数传递text 是要发送的文本内容,由客户端程序指定,然后通过WebSocket协议发送到服务器端。

通过这些方法的相互协作,实现了WebSocket的全双工通信功能,不同方法之间通过 Session 等对象以及参数传递来协同工作。


以下是基于该API的详细实现步骤,包含服务器端和客户端的代码示例及核心原理说明。

一、服务器端实现(核心步骤)

服务器端需要定义一个WebSocket端点(Endpoint),用于处理客户端的连接、消息接收、连接关闭等事件。主要通过注解或继承Endpoint类实现,推荐使用注解方式(更简洁)。

1. 依赖准备

若使用Maven,需添加Java EE WebSocket依赖(Java 7+支持,Java EE 7及以上内置):

<dependency> <groupId>javax.websocket</groupId> <artifactId>javax.websocket-api</artifactId> <version>1.1</version> <scope>provided</scope> </dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId></dependency>
2. 定义WebSocket端点类

通过@ServerEndpoint注解标记端点,并指定访问路径(如/chat),同时实现核心事件方法:

import javax.websocket.*;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.concurrent.CopyOnWriteArraySet;@ServerEndpoint(\"/chat\") // 客户端连接的URL:ws://localhost:8080/项目名/chatpublic class ChatServer { // 存储所有连接的客户端会话(线程安全集合) private static CopyOnWriteArraySet<Session> sessions = new CopyOnWriteArraySet<>(); private Session session; // 当前客户端的会话 /** * 客户端连接成功时触发 */ @OnOpen public void onOpen(Session session) { this.session = session; sessions.add(session); // 将新会话加入集合 System.out.println(\"新客户端连接,当前在线:\" + sessions.size()); } /** * 收到客户端消息时触发 */ @OnMessage public void onMessage(String message, Session session) throws IOException { System.out.println(\"收到消息:\" + message + \"(来自会话:\" + session.getId() + \")\"); // 广播消息给所有在线客户端 for (Session s : sessions) { if (s.isOpen()) { // 确保会话未关闭 s.getBasicRemote().sendText(\"服务器转发:\" + message); // 同步发送消息 // s.getAsyncRemote().sendText(\"异步发送:\" + message); // 异步发送(非阻塞) } } } /** * 客户端连接关闭时触发 */ @OnClose public void onClose(Session session, CloseReason reason) { sessions.remove(session); // 从集合中移除会话 System.out.println(\"客户端断开连接,原因:\" + reason.getReasonPhrase() + \",当前在线:\" + sessions.size()); } /** * 通信发生错误时触发 */ @OnError public void onError(Session session, Throwable error) { System.out.println(\"发生错误:\"); error.printStackTrace(); }}

二、客户端实现(Java客户端)

Java客户端可通过WebSocketContainer创建连接,与服务器端通信。需注意:客户端API在Java EE中也有定义,或可使用第三方库(如Tyrus,JSR 356的参考实现)。

1. 客户端依赖(若需独立运行)

若使用Tyrus作为客户端(适合非Web容器环境),添加Maven依赖:

<dependency> <groupId>org.glassfish.tyrus.bundles</groupId> <artifactId>tyrus-standalone-client</artifactId> <version>1.17</version></dependency>
2. 客户端代码实现
import javax.websocket.*;import java.net.URI;@ClientEndpoint // 标记为客户端端点public class ChatClient { private Session session; // 与服务器的会话 /** * 连接服务器 */ public void connect(String serverUri) throws Exception { WebSocketContainer container = ContainerProvider.getWebSocketContainer(); container.connectToServer(this, new URI(serverUri)); // 连接到服务器端点 } /** * 连接成功时触发 */ @OnOpen public void onOpen(Session session) { this.session = session; System.out.println(\"与服务器连接成功,会话ID:\" + session.getId()); try { session.getBasicRemote().sendText(\"客户端已连接\"); // 向服务器发送消息 } catch (IOException e) { e.printStackTrace(); } } /** * 收到服务器消息时触发 */ @OnMessage public void onMessage(String message, Session session) { System.out.println(\"收到服务器消息:\" + message); // 可在此处处理消息(如回复、业务逻辑) } /** * 连接关闭时触发 */ @OnClose public void onClose(Session session, CloseReason reason) { System.out.println(\"与服务器断开连接,原因:\" + reason.getReasonPhrase()); } /** * 发生错误时触发 */ @OnError public void onError(Session session, Throwable error) { System.out.println(\"客户端错误:\"); error.printStackTrace(); } /** * 向服务器发送消息 */ public void sendMessage(String message) throws IOException { if (session != null && session.isOpen()) { session.getBasicRemote().sendText(message); } } // 测试客户端 public static void main(String[] args) throws Exception { ChatClient client = new ChatClient(); client.connect(\"ws://localhost:8080/your-project/chat\"); // 服务器端点URL // 模拟发送消息 client.sendMessage(\"Hello, Server!\"); // 保持连接(实际应用中需根据业务处理) Thread.sleep(30000); }}

三、核心概念与参数传递说明

  1. Session(会话)

    • 服务器端和客户端均通过Session对象标识一个连接,包含连接的元数据(如ID、是否打开)。
    • 消息发送通过SessiongetBasicRemote()(同步)或getAsyncRemote()(异步)实现。
  2. 消息传递

    • 服务器端@OnMessage方法接收客户端消息,参数message为消息内容(支持Stringbyte[]ByteBuffer等类型)。
    • 客户端通过ws.send()(JS)或session.getBasicRemote().sendText()(Java)发送消息,服务器端接收后可转发给其他客户端(如示例中的广播逻辑)。
  3. 生命周期方法关联

    • @OnOpen → 连接建立时触发,Session对象在此处初始化并加入管理集合。
    • @OnMessage → 依赖@OnOpen中初始化的Session进行消息转发。
    • @OnClose/@OnError → 清理Session资源,确保集合中只保留活跃连接。

四、部署与测试

  1. 服务器端部署到支持WebSocket的容器(如Tomcat 8+、Jetty 9+),启动后访问ws://localhost:8080/项目名/chat
  2. 运行Java客户端或浏览器客户端,输入消息即可实现双向通信。

五:WebsocketConfig.java配置

package com.yourcompany.knowledge.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;import org.springframework.boot.web.servlet.server.ServletWebServerFactory;import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configurationpublic class WebSocketConfig { /** * 让Spring管理@ServerEndpoint,实现WebSocket服务端自动注册 */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } @Bean public ServletWebServerFactory servletWebServerFactory() { return new TomcatServletWebServerFactory(); }} 

说明:

  • 只要有这个配置,Spring Boot 会自动扫描并注册所有 @ServerEndpoint 注解的类为 WebSocket 服务端。
  • 适用于内嵌 Tomcat、Jetty、Undertow 等主流 Spring Boot 容器。
  • 不需要实现 WebSocketConfigurer 或加 @EnableWebSocket,也不需要注册 handler。


只需保证

  • pom.xml 有 spring-boot-starter-websocket 依赖(你已补充)。
  • WebSocketConfig 有 ServerEndpointExporter Bean(如上)。
  • 你的 @ServerEndpoint 类加了 @Component 或被 Spring 扫描到。

如下的websocketConfig.java配置:

@Configuration@EnableWebSocket // 启用WebSocket支持public class WebSocketConfig implements WebSocketConfigurer { private final YourWebSocketHandler webSocketHandler; private final HandshakeInterceptor handshakeInterceptor; public WebSocketConfig(YourWebSocketHandler webSocketHandler, HandshakeInterceptor handshakeInterceptor) { this.webSocketHandler = webSocketHandler; this.handshakeInterceptor = handshakeInterceptor; } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(webSocketHandler, \"/ws\") .setAllowedOrigins(\"*\") .addInterceptors(handshakeInterceptor); }}
  • @EnableWebSocket(是 Spring WebSocket API 方式)。

你的这段代码是**Spring WebSocket(基于Spring WebSocket API)**的标准配置方式,和 @ServerEndpoint 方式属于两种不同的 WebSocket 服务端实现风格。它们各有优缺点,适用场景如下:

主要特性

  • 使用 @EnableWebSocket 和实现 WebSocketConfigurer,通过 Java 配置注册 WebSocket 端点。
  • 端点 /wsYourWebSocketHandler 处理(实现 WebSocketHandler 接口)。
  • 支持自定义握手拦截器(如 token 校验、用户认证等)。
  • 支持灵活的跨域配置。

@ServerEndpoint 方式的对比

方式 适用场景/优缺点 @ServerEndpoint - 兼容 Java EE 标准,注解驱动,适合 Tomcat/Undertow/Jetty
- 支持 @OnOpen@OnMessage 等注解
- 适合与 Java EE 生态集成(如 JSR 356)
- 不易与 Spring Security、Session、依赖注入深度集成 Spring WebSocket API - 纯 Spring 生态,基于 WebSocketHandler,更易与 Spring Security、Session、依赖注入集成
- 支持拦截器、消息分发、STOMP 协议等高级特性
- 代码更灵活,适合复杂业务和权限控制
- 没有 @OnOpen@OnMessage 注解,需实现接口方法

参考价值

  • 如果需要更灵活的权限控制、与 Spring Security 深度集成、或需要 STOMP、SockJS 等高级特性,推荐用这种方式。
  • 如果只需要简单的 WebSocket 服务端,@ServerEndpoint 方式更简单直接。
  • 两种方式不能混用同一个端点路径,但可以共存于同一个项目(不同路径)。

什么时候用这种配置方式?

  • 需要拦截器(如 token 校验、用户认证)
  • 需要和 Spring Security、Session、Bean 注入深度集成
  • 需要 STOMP、消息广播、订阅等高级特性
  • 需要更灵活的消息处理逻辑

总结

  • 如果当前项目对权限、认证、消息分发有更高要求,建议采用这种方式
  • 如果只需简单的 WebSocket 服务端,@ServerEndpoint 方式即可。

如需将现有项目切换为 Spring WebSocket API 方式,或需要集成拦截器、权限校验等,Spring Boot 内嵌 Tomcat + @ServerEndpoint 或 Spring WebSocket 配置,已经支持 WebSocket 服务端功能。

  • 只要 pom.xml 里有 spring-boot-starter-web(或 spring-boot-starter-websocket),就能让前端/客户端通过 ws:// 协议访问你的服务端接口。
  • 只需要保证端口、防火墙、跨域等网络层面

配电柜