15 · WebSocket(ace-websocket)

WebSocket 是独立可插拔组件 ace-websocket(不在核心,对标 Spring Boot 的 spring-boot-starter-websocket)。注解驱动,风格对标 JSR 356(Jakarta WebSocket)的 @ServerEndpoint/@OnMessage

引入

cjpm.toml 加依赖,main 所在文件 import 触发自注册:

[dependencies]
  "aceboot::websocket" = { path = "../../ace-websocket" }
import aceboot::websocket.*   // WsSession;@WsController 等宏在 aceboot::framework_macros

声明式端点

@WsController["/ws/chat/:room"]          // 路径支持 :param(复用 path-to-regexp)
public class ChatSocket {
    @OnOpen
    public func onOpen(session: WsSession): Unit {
        session.send("welcome")
    }

    @OnMessage
    public func onMessage(message: String, session: WsSession): Unit {
        session.send("echo: ${message}")    // 主动回发
    }

    @OnClose
    public func onClose(session: WsSession): Unit { ... }
}
  • @WsController["/path"] — 端点类 + 路径(对标 @ServerEndpoint)。
  • @OnOpen func onOpen(session: WsSession) — 连接建立。
  • @OnMessage func onMessage(message: String, session: WsSession) — 收到文本消息(签名固定为 (String, WsSession))。
  • @OnClose func onClose(session: WsSession) — 连接关闭。
  • @OnError func onError(session: WsSession, error: Exception) — 帧循环异常。
  • 三个回调均可选;端点类是单例 Bean,可 @Inject 其它服务。

WsSession

session.send(text: String)              // 发送文本帧
session.sendBytes(data: Array<UInt8>)   // 发送二进制帧
session.close()                          // 主动关闭(Close 帧)

机制与分层

带 Upgrade: websocket 的请求
  → ace-http dispatch 在「构造 Context / 读 body / 跑洋葱」之前分流
  → 匹配 @WsController 端点(path-to-regexp)→ stdx WebSocket.upgradeFromServer 握手
  → 帧循环:文本帧 → @OnMessage;Ping 自动回 Pong;Close → 退出 → @OnClose
  • 独立组件ace-websocket 单向依赖 ace-http,启动期经 registerWsEndpoint 把端点「推」给 ace-http 注册表(依赖倒置,ace-http 不反依赖 ace-websocket,核心 ace-web/router 不含 ws)。
  • 底层:stdx WebSocketupgradeFromServer 自动完成 101 握手 + Sec-WebSocket-Acceptread()/write() 收发帧)。
  • 每连接一协程跑帧循环(契合 stdx 服务端模型)。

限制

  • WebSocket 升级在洋葱之前分流,故不经认证/中间件。需要鉴权请在 @OnOpen 内自行校验(v1 WsSession 暂未暴露握手请求头,后续可加)。
  • @OnMessage 仅触发文本帧;二进制帧/分片的高层回调待后续。

测试

import asyncio, websockets
async def main():
    async with websockets.connect("ws://127.0.0.1:8080/ws/chat/room1") as ws:
        print(await ws.recv())          # welcome
        await ws.send("hi"); print(await ws.recv())   # echo: hi
asyncio.run(main())

回到 文档导航