WebSocket 客户端
WebSocket 客户端用于建立双向实时通信连接,支持文本和二进制消息收发,以及完整的生命周期管理。
使用 Feat 工厂方法创建 WebSocket 连接:
import tech.smartboot.feat.Feat;import tech.smartboot.feat.core.client.WebSocketListener;
Feat.websocket("ws://localhost:8080/chat", new WebSocketListener() { @Override public void onOpen(WebSocketClient client, WebSocketResponse response) { System.out.println("连接已建立"); }
@Override public void onMessage(WebSocketClient client, String message) { System.out.println("收到消息: " + message); }});带配置的连接
Section titled “带配置的连接”WebSocketClient client = new WebSocketClient("ws://localhost:8080/chat");client.options() .debug(true) .connectTimeout(5000) .readBufferSize(8192);
client.connect(new WebSocketListener() { @Override public void onOpen(WebSocketClient client, WebSocketResponse response) { System.out.println("连接成功!"); }
@Override public void onMessage(WebSocketClient client, String message) { System.out.println("收到: " + message); }
@Override public void onClose(WebSocketClient client, WebSocketResponse response, CloseReason reason) { System.out.println("连接关闭: " + reason); }
@Override public void onError(WebSocketClient client, Throwable error) { error.printStackTrace(); }});WebSocketClient client = new WebSocketClient("ws://localhost:8080");
client.options() .debug(true) // 开启调试日志 .connectTimeout(5000) // 连接超时(毫秒) .readTimeout(30000) // 读取超时(毫秒) .readBufferSize(8192) // 读缓冲区大小 .writeBufferSize(8192) // 写缓冲区大小 .maxFrameSize(65536); // 最大帧大小自定义请求头
Section titled “自定义请求头”import tech.smartboot.feat.core.common.HeaderName;
WebSocketClient client = new WebSocketClient("ws://localhost:8080");
client.header(header -> header .set(HeaderName.USER_AGENT, "Feat-WebSocket-Client") .set(HeaderName.AUTHORIZATION, "Bearer token123"));
client.connect(listener);@Overridepublic void onOpen(WebSocketClient client, WebSocketResponse response) { try { // 发送文本消息 client.sendMessage("Hello, WebSocket!"); client.sendMessage("{\"type\":\"chat\",\"content\":\"hello\"}");
// 发送二进制消息 byte[] data = "Binary data".getBytes(StandardCharsets.UTF_8); client.sendBinary(data); } catch (IOException e) { e.printStackTrace(); }}@Overridepublic void onMessage(WebSocketClient client, String message) { // 处理文本消息 System.out.println("收到文本: " + message);}
@Overridepublic void onMessage(WebSocketClient client, byte[] message) { // 处理二进制消息 System.out.println("收到二进制: " + message.length + " bytes");}生命周期管理
Section titled “生命周期管理”回调触发顺序
Section titled “回调触发顺序”以下泳道图展示了 WebSocket 连接过程中各回调的触发时机:
sequenceDiagram
autonumber
participant App as 应用程序
participant Client as WebSocket Client
participant Server as WebSocket 服务器
App->>Client: connect() 发起连接
Client->>Server: WebSocket Handshake
alt 连接成功
Server-->>Client: Handshake Response (101)
Client-->>App: onOpen()
loop 双向通信
App->>Client: sendMessage()
Client->>Server: WebSocket Frame
Server-->>Client: WebSocket Frame
Client-->>App: onMessage()
end
Server-->>Client: Close Frame
Client-->>App: onClose()
else 连接失败
Client-->>App: onError()
Client-->>App: onClose()
end
回调触发顺序说明:
- 发起连接 - 调用
connect()后,客户端发送 WebSocket 握手请求 - 连接成功 - 握手成功(HTTP 101)时触发
onOpen(),此时可以开始发送消息 - 接收消息 - 收到服务端消息时触发
onMessage() - 连接失败 - 握手失败或网络异常时触发
onError() - 连接关闭 - 无论成功或失败,连接关闭时都会触发
onClose()
完整生命周期示例
Section titled “完整生命周期示例”import tech.smartboot.feat.core.client.WebSocketClient;import tech.smartboot.feat.core.client.WebSocketListener;import tech.smartboot.feat.core.common.codec.websocket.CloseReason;
public class ChatClient { private WebSocketClient client;
public void connect(String url) throws IOException { client = new WebSocketClient(url); client.options().debug(true).connectTimeout(5000);
client.connect(new WebSocketListener() { @Override public void onOpen(WebSocketClient client, WebSocketResponse response) { System.out.println("[连接成功] " + response.getStatusCode()); try { client.sendMessage("{\"type\":\"join\",\"room\":\"lobby\"}"); } catch (IOException e) { e.printStackTrace(); } }
@Override public void onMessage(WebSocketClient client, String message) { System.out.println("[收到消息] " + message); }
@Override public void onClose(WebSocketClient client, WebSocketResponse response, CloseReason reason) { System.out.println("[连接关闭] Code: " + reason.getCode()); }
@Override public void onError(WebSocketClient client, Throwable error) { System.err.println("[错误] " + error.getMessage()); } }); }
public void send(String message) throws IOException { if (client != null && client.isConnected()) { client.sendMessage(message); } }
public void close() throws IOException { if (client != null) { client.close(1000, "Normal closure"); } }}手动关闭连接
Section titled “手动关闭连接”WebSocket 是长连接,程序不会自动退出,需要显式关闭。
// 正常关闭client.close(1000, "Normal closure");
// 强制关闭client.close();public void onClose(WebSocketClient client, WebSocketResponse response, CloseReason reason) { if (reconnectAttempts < MAX_RECONNECT) { reconnectAttempts++; System.out.println("连接断开,尝试重连 (" + reconnectAttempts + "/" + MAX_RECONNECT + ")"); try { Thread.sleep(3000); doConnect(); // 重新建立连接 } catch (Exception e) { e.printStackTrace(); } }}| 代码 | 名称 | 说明 |
|---|---|---|
| 1000 | Normal Closure | 正常关闭 |
| 1001 | Going Away | 服务端或客户端离开 |
| 1002 | Protocol Error | 协议错误 |
| 1006 | Abnormal Closure | 异常关闭(连接意外断开) |
| 1009 | Message Too Big | 消息太大 |
| 1011 | Internal Error | 服务端内部错误 |
import tech.smartboot.feat.Feat;import tech.smartboot.feat.core.server.WebSocketRequest;import tech.smartboot.feat.core.server.WebSocketResponse;import tech.smartboot.feat.core.server.upgrade.websocket.WebSocketUpgrade;
public class EchoWebSocketServer { public static void main(String[] args) { Feat.httpServer().httpHandler(req -> { req.upgrade(new WebSocketUpgrade(65536) { @Override public void handleTextMessage(WebSocketRequest request, WebSocketResponse response, String data) { System.out.println("收到: " + data); response.sendTextMessage("Echo: " + data); } }); }).listen(8080); }}交互式客户端
Section titled “交互式客户端”import tech.smartboot.feat.core.client.WebSocketClient;import tech.smartboot.feat.core.client.WebSocketListener;import tech.smartboot.feat.core.common.codec.websocket.CloseReason;
import java.io.BufferedReader;import java.io.InputStreamReader;
public class InteractiveWebSocketClient { private static WebSocketClient client;
public static void main(String[] args) throws Exception { client = new WebSocketClient("ws://localhost:8080"); client.options().debug(true);
client.connect(new WebSocketListener() { @Override public void onOpen(WebSocketClient client, WebSocketResponse response) { System.out.println("已连接到服务器,输入消息(输入 'exit' 退出):"); }
@Override public void onMessage(WebSocketClient client, String message) { System.out.println("服务器: " + message); }
@Override public void onClose(WebSocketClient client, WebSocketResponse response, CloseReason reason) { System.out.println("连接已关闭: " + reason); } });
// 读取用户输入 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String line; while ((line = reader.readLine()) != null) { if ("exit".equalsIgnoreCase(line)) { client.close(1000, "User requested"); break; } client.sendMessage(line); } }}连接失败
检查清单:
- URL 是否为
ws://或wss:// - 服务端是否支持 WebSocket 升级
- 端口和路径是否正确
sendMessage 抛出 IOException
连接未就绪或已断开。只在 onOpen 回调后发送消息:
if (client.isConnected()) { client.sendMessage("data");}程序不退出
WebSocket 是长连接,需要手动关闭:
// 正常关闭client.close(1000, "Normal closure");
// 强制关闭client.close();