连接第一个 WebSocket 服务
This content is not available in your language yet.
如果说 HttpClient 适合请求-响应式通信,SSE 适合服务端单向推送,那么 WebSocket 适合真正的长连接实时交互。
这篇文档不打算做完整协议百科,而是带你把第一条连接建起来。
什么时候应该用 WebSocket
Section titled “什么时候应该用 WebSocket”先判断一下你的场景是不是它:
- 客户端和服务端需要双向实时通信
- 消息会持续来回交互,而不是一次请求一次响应
- 你要做聊天室、协作编辑、游戏、实时控制台这类长连接场景
如果只是服务端持续推送,而客户端不需要主动发消息,先看 SSE 客户端教程。
先准备一个最小服务端
Section titled “先准备一个最小服务端”如果你手头还没有 WebSocket 服务,可以先用这个最小回声服务器本地自测:
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 SimpleWebSocketServer { public static void main(String[] args) { Feat.httpServer().httpHandler(req -> req.upgrade(new WebSocketUpgrade(2000) { @Override public void handleTextMessage(WebSocketRequest request, WebSocketResponse response, String data) { response.sendTextMessage(data); } })).listen(8080); }}它的行为很简单:你发什么,它回什么。
建立第一条连接
Section titled “建立第一条连接”客户端最短写法如下:
import tech.smartboot.feat.core.client.WebSocketClient;import tech.smartboot.feat.core.client.WebSocketListener;import tech.smartboot.feat.core.client.WebSocketResponse;import tech.smartboot.feat.core.common.codec.websocket.CloseReason;
import java.io.IOException;
public class WebSocketDemo { public static void main(String[] args) throws IOException { WebSocketClient client = new WebSocketClient("ws://localhost:8080");
client.connect(new WebSocketListener() { @Override public void onOpen(WebSocketClient client, WebSocketResponse response) { try { client.sendMessage("hello world"); } catch (IOException e) { throw new RuntimeException(e); } }
@Override public void onMessage(WebSocketClient client, String message) { System.out.println("received: " + message); }
@Override public void onClose(WebSocketClient client, WebSocketResponse response, CloseReason reason) { System.out.println("closed: " + reason); } }); }}这个例子其实已经覆盖了 WebSocket 客户端的完整最小生命周期:
- 创建客户端
- 建立连接
onOpen(...)后发送消息- 在
onMessage(...)里接收服务端消息 - 最后由连接关闭或手动关闭收尾
为什么消息要放在 onOpen(...) 之后再发
Section titled “为什么消息要放在 onOpen(...) 之后再发”因为只有握手成功以后,连接才真的可用。
在 Feat 的客户端模型里,onOpen(...) 就是你可以安全开始发第一条消息的时机。
运行之后你应该看到什么
Section titled “运行之后你应该看到什么”如果服务端是上面的回声服务器,那么客户端会打印:
received: hello world只要看到这一行,就说明下面几件事已经全部成立:
- WebSocket 握手成功
- 客户端成功发出了文本消息
- 服务端成功处理并返回
- 客户端监听器成功收到了回包
发送文本和二进制消息
Section titled “发送文本和二进制消息”文本消息最常见:
client.sendMessage("Hello, Feat!");client.sendMessage("{\"type\":\"chat\",\"content\":\"hello\"}");如果你要发二进制数据,可以用:
byte[] binaryData = "Binary Data".getBytes();client.sendBinary(binaryData);这类用法更适合自定义协议、音视频片段或压缩后的消息体。
调试和连接配置
Section titled “调试和连接配置”如果你想看连接过程,或者需要改一些连接参数:
WebSocketClient client = new WebSocketClient("ws://localhost:8080");client.options() .debug(true) .connectTimeout(5000) .readBufferSize(8192) .writeBufferSize(8192);这些配置里,最常用的通常是:
debug(true):本地排查连接和收发过程connectTimeout(...):防止长时间握手卡住
用 Feat.websocket(...) 的便捷方式
Section titled “用 Feat.websocket(...) 的便捷方式”如果你不想分两步写 new WebSocketClient(...) 和 connect(...),Feat 还提供了便捷入口:
import tech.smartboot.feat.Feat;
Feat.websocket("ws://localhost:8080", new WebSocketListener() { @Override public void onOpen(WebSocketClient client, WebSocketResponse response) { try { client.sendMessage("hello"); } catch (IOException e) { throw new RuntimeException(e); } }});它更适合写非常短的 demo。
如果你还要进一步调 options(),那还是显式创建 WebSocketClient 更清楚。
如果你的客户端是长期运行的程序,记得在合适的时候手动关闭连接:
client.close();如果需要带关闭码和原因:
client.close(1000, "normal shutdown");连接总是建不起来
Section titled “连接总是建不起来”优先检查:
- URL 是否真的是
ws://或wss:// - 服务端是否真的支持 WebSocket 升级
- 端口和路径是否对应正确
sendMessage(...) 抛 IOException
Section titled “sendMessage(...) 抛 IOException”通常意味着连接还没准备好,或者已经断开。
最稳妥的做法是:只在 onOpen(...) 之后发送第一条消息。
我明明收到了消息,但程序不退出
Section titled “我明明收到了消息,但程序不退出”这是正常的。
WebSocket 是长连接,不会像普通 HTTP 请求那样自动结束。你要自己决定何时关闭。
我只是想单向接收事件流
Section titled “我只是想单向接收事件流”那通常不是 WebSocket 的最佳入口,先看 SSE 客户端教程。
- HttpClient 实战教程:如果你的通信其实还是普通 HTTP
- SSE 客户端教程:如果你的场景只是服务端推送
- Feat Core 导览:如果你还要自己写对应的服务端能力